Section 7.8

Data set books contains the daily sales of paperback and hardcover books at the same store. The task is to forecast the next four days’ sales for paperback and hardcover books

Plot the series and discuss the main features of the data.

Both the time series show a linear upward trend. Visually, the Paperback seems to have some regular patters (seasonality) approximately equal to 3 days.

autoplot(books, facets = T)

There doesn’t seem to be any linear correlation between the two series.

lattice::xyplot(Paperback~Hardcover, as.data.frame(books), type=c('p','smooth'))

The PACF plots do show that for Paperback, there is a 3-order autocorrelation in the signal which is significant. For Hardcover, there is a 1st and 2nd order significance.

ggPacf(books[,'Paperback'])
ggPacf(books[,'Hardcover'])

Use simple exponential smoothing with the ses function (setting initial=“simple”) and explore different values of alpha for the paperback series.

Here are three settings - a=0.9, a=0.5, and a=0.01. As alpha increases, so does the uncertainty of the prediction since ses will look back further in time. Though at alpha = 0.01, the point estimate seems quite low.

ses(books[,'Paperback'], initial = 'simple', alpha = .9)  %>% forecast() %>% autoplot()
ses(books[,'Paperback'], initial = 'simple', alpha = .5)  %>% forecast() %>% autoplot()
ses(books[,'Paperback'], initial = 'simple', alpha = .01) %>% forecast() %>% autoplot()

Record the within-sample SSE for the one-step forecasts. Plot SSE against alpha and find which value of alpha works best. What is the effect of alpha on the forecasts?

We can see a typical curve as seen during parameter tuning. The SSE is minimum at an alpha value of about 0.2.

sse_list <- c()
a_list <- seq(0.001, 0.999, length.out = 20)
for (a_sel in a_list) {
    ses(books[,'Paperback'], initial = 'simple', alpha = a_sel)$model$SSE -> sse
    sse_list <- c(sse_list, sse)
}
plot(a_list, sse_list, type='b')
points(a_list[which.min(sse_list)], sse_list[which.min(sse_list)], col='red', pch=20)

Now let ses select the optimal value of alpha. Use this value to generate forecasts for the next four days. Compare your results with 2.

Alpha selected is 0.215. The point estimate for this forecast seem better than the #2 results. Prediction interval widths are about similar. RMSE is smaller for the auto-alpha selection.

ses(books[,'Paperback'], initial = 'simple', alpha = NULL)$model$par['alpha']
ses(books[,'Paperback'], initial = 'simple', alpha = NULL)%>% forecast(h=4)%>% autoplot()
bind_rows(ses(books[,'Paperback'], initial = 'simple', alpha = NULL) %>% 
              forecast(h=4) %>% accuracy() %>% sweep::sw_tidy(),
          ses(books[,'Paperback'], initial = 'simple', alpha = 0.01) %>% 
              forecast(h=4) %>% accuracy() %>% sweep::sw_tidy()) %>% 
    knitr::kable(format = 'latex',caption = '1st line: Auto-alpha. 2nd line: a=0.01')

Repeat but with initial=“optimal”. How much difference does an optimal initial level make?

Setting the initial to optimal reduces RMSE by ~1 unit, and MASE by ~0.02 units.

ses(books[,'Paperback'], initial = 'optimal', alpha = NULL) -> fit_optimal
ses(books[,'Paperback'], initial = 'simple', alpha = NULL) -> fit_simple
bind_rows(fit_optimal %>% forecast(h=4) %>% accuracy() %>% sweep::sw_tidy(), 
          fit_simple %>% forecast(h=4) %>% accuracy() %>% sweep::sw_tidy()) %>% 
    knitr::kable(format = 'latex',
                 caption = '1st line: Optimal initial. 2nd line: Simple initial')

Repeat steps (b)–(d) with the hardcover series.

Use simple exponential smoothing with the ses function (setting initial=“simple”) and explore different values of alpha for the paperback series.

This series does better with a higher value of alpha.

ses(books[,'Hardcover'], initial = 'simple', alpha = .9)  %>% forecast() %>% autoplot()
ses(books[,'Hardcover'], initial = 'simple', alpha = .5)  %>% forecast() %>% autoplot()
ses(books[,'Hardcover'], initial = 'simple', alpha = .01) %>% forecast() %>% autoplot()

Record the within-sample SSE for the one-step forecasts. Plot SSE against alpha and find which value of alpha works best. What is the effect of alpha on the forecasts?

We can see a typical curve as seen during parameter tuning. The SSE is minimum at an alpha value of about 0.35.

sse_list <- c()
a_list <- seq(0.001, 0.999, length.out = 20)
for (a_sel in a_list) {
    ses(books[,'Hardcover'], initial = 'simple', alpha = a_sel)$model$SSE -> sse
    sse_list <- c(sse_list, sse)
}
plot(a_list, sse_list, type='b')
points(a_list[which.min(sse_list)], sse_list[which.min(sse_list)], col='red', pch=20)

Now let ses select the optimal value of alpha. Use this value to generate forecasts for the next four days. Compare your results with 2.

Alpha selected is 0.347. The point estimate for this forecast seem better than previous results. Prediction interval widths are about similar. RMSE is smaller for the auto-alpha selection.

ses(books[,'Hardcover'], initial = 'simple', alpha = NULL)$model$par['alpha']
ses(books[,'Hardcover'], initial = 'simple', alpha = NULL)%>% forecast(h=4)%>% autoplot()
bind_rows(ses(books[,'Hardcover'], initial = 'simple', alpha = NULL) %>% 
              forecast(h=4) %>% accuracy() %>% sweep::sw_tidy(),
          ses(books[,'Hardcover'], initial = 'simple', alpha = 0.9) %>% 
              forecast(h=4) %>% accuracy() %>% sweep::sw_tidy()) %>% 
    knitr::kable(format = 'latex',caption = '1st line: Auto-alpha. 2nd line: a=0.01')

Repeat but with initial=“optimal”. How much difference does an optimal initial level make?

Setting the initial to optimal reduces RMSE by ~2 units, but MASE increased by ~0.01 units.

ses(books[,'Hardcover'], initial = 'optimal', alpha = NULL) -> fit_optimal
ses(books[,'Hardcover'], initial = 'simple', alpha = NULL) -> fit_simple
bind_rows(fit_optimal %>% forecast(h=4) %>% accuracy() %>% sweep::sw_tidy(), 
          fit_simple %>% forecast(h=4) %>% accuracy() %>% sweep::sw_tidy()) %>% 
    knitr::kable(format = 'latex',
                 caption = '1st line: Optimal initial. 2nd line: Simple initial')

Apply Holt’s linear method to the paperback and hardback series and compute four-day forecasts in each case.

holt(books[,'Paperback']) %>% forecast(h=4) %>% autoplot()
holt(books[,'Hardcover']) %>% forecast(h=4) %>% autoplot()

Compare the SSE measures of Holt’s method for the two series to those of simple exponential smoothing in the previous question. Discuss the merits of the two forecasting methods for these data sets.

The holt method definitely has a lower SSE than the sse method since holt can account for the upward trend in both the timeseries. ses unfortunately, cannot account for this trend.

ses(books[,'Paperback']) %>% residuals() %>% .^2 %>% sum()
ses(books[,'Hardcover']) %>% residuals() %>% .^2 %>% sum()

holt(books[,'Paperback']) %>% residuals() %>% .^2 %>% sum()
holt(books[,'Hardcover']) %>% residuals() %>% .^2 %>% sum()

Compare the forecasts for the two series using both methods. Which do you think is best?

Again, the forecasts created by holt have the right upward trend as would be expected from the

Calculate a 95% prediction interval for the first forecast for each series using both methods, assuming normal errors. Compare your forecasts with those produced by R.

Using the forecast function:

holt(books[,'Paperback']) %>% forecast(h=1)
holt(books[,'Hardcover']) %>% forecast(h=1)

For this exercise, use the quarterly UK passenger vehicle production data from 1977:1–2005:1 (data set ukcars).

Plot the data and describe the main features of the series.

  • Between 1977 and 1980 there is a downward trend
  • From 1980 to 2000, thre is a steady linear increasing trend
  • Something happens in 2000 which causes a sharp decline for a year and picks back up
  • There is a quarterly seasonality we can see
autoplot(ukcars)

Decompose the series using STL and obtain the seasonally adjusted data.

stl_fit <- stl(ukcars, s.window = 'periodic')
autoplot(stl_fit)
sadjusted <- seasadj(stl_fit)

Forecast the next two years of the series using an additive damped trend method applied to the seasonally adjusted data. Then reseasonalize the forecasts. Record the parameters of the method and report the RMSE of the one-step forecasts from your method.

holtfit <- holt(sadjusted, h = 8, damped = T, exponential = F)
seasonaladjustments <- ukcars-sadjusted
holtfit_w_seasonal <- holtfit$mean + seasonaladjustments[2:9]
holtfit_w_seasonal

Parameters of the fit are here. RMSE is 25.15986.

summary(holtfit)

Forecast the next two years of the series using Holt’s linear method applied to the seasonally adjusted data. Then reseasonalize the forecasts. Record the parameters of the method and report the RMSE of of the one-step forecasts from your method.

holtfit_additive <- holt(sadjusted, h = 8, damped = F, exponential = F)
seasonaladjustments <- ukcars-sadjusted
holtfit_additive_w_seasonal <- holtfit_additive$mean + seasonaladjustments[2:9]
holtfit_additive_w_seasonal

Parameters of the fit are here. RMSE is 25.26.

summary(holtfit_additive)

Now use ets() to choose a seasonal model for the data.

ETS selects an A-Ad-A model - Additive error, Additive damped seasonal and Additive trend component.

ets(ukcars, model = 'ZZZ',damped = T) %>% summary()

Compare the RMSE of the fitted model with the RMSE of the model you obtained using an STL decomposition with Holt’s method. Which gives the better in-sample fits?

  • RMSE of ETS A-Ad-A model = 25.18409
  • RMSE of Holt Addive Damped modelo on STL decomposed data = 25.15986

The ETS gives marginally worse results.

Compare the forecasts from the two approaches? Which seems most reasonable?

Given how close the RMSE values are we expect very similar forecasts. And we can see this in the plots. They are virtually indistinguishable.

holtfit$mean  <- holtfit$mean + seasonaladjustments[2:9]
holtfit$upper <- holtfit$upper + seasonaladjustments[2:9]
holtfit$lower <- holtfit$lower + seasonaladjustments[2:9]
holtfit$x  <- holtfit$x + seasonaladjustments
ets(ukcars, model = 'ZZZ',damped = T) %>% forecast() %>% autoplot()
holtfit %>% autoplot()

For this exercise, use the monthly Australian short-term overseas visitors data, May 1985–April 2005. (Data set: visitors.)

Make a time plot of your data and describe the main features of the series.

  • Increasing trend, almost linearly increasing
  • Clear seasonality (yearly) # Increasing variance of the seasonality over time
  • Sharp drop mid-2004 - something odd happened here
autoplot(visitors)

Forecast the next two years using Holt-Winters’ multiplicative method.

hw(visitors, seasonal = 'm', damped = F) %>% 
    forecast(h=12*2) %>% autoplot()

Why is multiplicative seasonality necessary here?

This is because the seasonality keeps increasing over time. We can visually see this if we keep the seasonality constant (using decompose). Look at the residuals - the magnitude keeps increasing over time.

Multiplicative seasonality allows for it to increase over time.

decompose(visitors) %>% plot

Experiment with making the trend exponential and/or damped.

As expected, the exponential method overpredicts the point estimates than the damped forecast. But, the exponential method seems to have a smaller prediction interval.

hw(visitors, seasonal = 'm', damped = T) %>% 
    forecast(h=12*2) %>% autoplot()
hw(visitors, seasonal = 'm', exponential = T) %>% 
    forecast(h=12*2) %>% autoplot()

Compare the RMSE of the one-step forecasts from the various methods. Which do you prefer?

The RMSEs are fairly similar for all three models. Based on RMSE I would pick the damped Holt Winters model. MASE for this model is the lowest too.

bind_rows(
hw(visitors, seasonal = 'm') %>% forecast(h=12*2) %>% 
    accuracy() %>% sweep::sw_tidy(),
hw(visitors, seasonal = 'm', damped = T) %>% forecast(h=12*2) %>% 
    accuracy() %>% sweep::sw_tidy(),
hw(visitors, seasonal = 'm', exponential = T) %>% forecast(h=12*2) %>% 
    accuracy() %>% sweep::sw_tidy()
) %>% mutate(.rownames = c('hw_m','hw_m_d','hw_m_e')) %>% 
    rename(model = .rownames)

Now fit each of the following models to the same data:

a multiplicative Holt-Winters’ method;

hw_m <- hw(visitors, seasonal = 'm') %>% forecast(h=12*2)
hw_m %>% autoplot()

an ETS model;

ets_fit <- ets(visitors) %>% forecast(h=12*2)
ets_fit %>% autoplot()

an additive ETS model applied to a Box-Cox transformed series;

ets_box_fit <- ets(visitors, lambda = 'auto') %>% forecast(h=12*2)
ets_box_fit %>% autoplot()

a seasonal naive method applied to the Box-Cox transformed series;

snaive_box_fit <- snaive(visitors, lambda = 'auto') %>% forecast(h=12*2)
snaive_box_fit %>% autoplot()

an STL decomposition applied to the Box-Cox transformed data followed by an ETS model applied to the seasonally adjusted (transformed) data.

This code performs the needed actions. The stl function removes the trend from the signal very well. The residuals look fairly random visually.

After adjusting for seasonality, we can see that an ETS model is an A-Ad-N model: no seasonality with a linear damped trend. Now, although we have an additive trend component, the beta coeef is 1e-4, so practically, the forecast is flat as we can see in the plot.

BoxCox.lambda(x = visitors)
visitors_boxed <- BoxCox(x = visitors, lambda = 0.2775249)
visitors_stl <- stl(visitors_boxed, s.window = 'periodic')
plot(visitors_stl)
visitors_sadj <- seasadj(visitors_stl)
ets_sadj <- ets(visitors_sadj, model = 'ZZZ', damped = T)
ets_sadj %>% summary()
ets_sadj %>% forecast() %>% autoplot()

For each model, look at the residual diagnostics and compare the forecasts for the next two years. Which do you prefer?

The residuals tell us this:

  • HW - There is significant autocorrelation in the model (12,24,36 periods). P-value is low too.
  • ETS - Almost no autocorrelation. Just a bit at 12 periods. P-value is low, but we do have a large sample size, so the test will be sensitive.
  • ETS-BoxCoxed - Almost no autocorrelation.
  • SNaive - LOTS of autocorrelation (as expected).
  • ETS SAdjusted - No autocorrelation.
checkresiduals(hw_m)
checkresiduals(ets_fit)
checkresiduals(ets_box_fit)
checkresiduals(snaive_box_fit)
checkresiduals(ets_sadj)

Putting everything together, we can see that:

  • Seasonal naive underpredicts since it can’t account for the increasing trend component
  • The BoxCox+STL+ETS method also underpredicts
  • HW seems reasonable
  • ETS seems reasonable as well
  • ETS-BoxCoxed seems to over predict

Given this information and the residuals, I would probably pick the ETS model. It has almost no autocorrelation and seems to fit the data the best.

hw_m = hw_m %>% forecast(h = 12*2) %>% sweep::sw_tidy() %>% 
    pull('Point Forecast') %>% ts(frequency = 12, start=c(2005,5))
ets_fit = ets_fit %>% forecast(h = 12*2) %>% sweep::sw_tidy() %>% 
    pull('Point Forecast') %>% ts(frequency = 12, start=c(2005,5))
ets_box_fit = ets_box_fit %>% forecast(h = 12*2) %>% sweep::sw_tidy() %>% 
    pull('Point Forecast') %>% ts(frequency = 12, start=c(2005,5))
snaive_box_fit = snaive_box_fit %>% forecast(h = 12*2) %>% sweep::sw_tidy() %>% 
    pull('Point Forecast') %>% ts(frequency = 12, start=c(2005,5))
ets_reseasoned = ets_sadj %>% forecast(h = 12*2) %>% sweep::sw_tidy() %>% 
    pull('Point Forecast') %>% ts(frequency = 12, start=c(2005,5))
ets_reseasoned = (ets_reseasoned + visitors_stl$time.series %>% 
                      tail(24) %>% .[,'seasonal'] %>% 
                      as.numeric()) %>% InvBoxCox(lambda = 0.2775249)

ts.union(visitors, hw_m, ets_fit, ets_box_fit, snaive_box_fit, ets_reseasoned) %>% 
    autoplot(size=2) + xlim(c(2000,2008))

Section 8.11

Use R to simulate and plot some data from simple ARIMA models.

Use the following R code to generate data from an AR(1) model with phi_1=0.6 and sigma_2=1. The process starts with y0=0.

y <- ts(data = numeric(100))
e <- rnorm(n = 100, mean = 0, sd = 1)
for(i in 2:100)
  y[i] <- 0.6*y[i-1] + e[i]

Produce a time plot for the series. How does the plot change as you change phi_1?

plot(y)

phi1 <- seq(-1, 1, by = 0.5)
phi1
[1] -1.0 -0.5  0.0  0.5  1.0
y <- ts(data = numeric(100))
e <- rnorm(n = 100, mean = 0, sd = 1)
result <- matrix(nrow = 100,ncol = length(phi1))
for(p in seq_along(phi1)){
    for(i in 2:100)
      y[i] <- phi1[p]*y[i-1] + e[i]
    result[,p] <- y
}
plot (result[,1], type='l', col='gray')
lines(result[,2], type='l', col='red')
lines(result[,3], type='l', col='blue')
lines(result[,4], type='l', col='green')
lines(result[,5], type='l', col='orange')

Over the range of values of phi_1 from -1 to 1:

  • when c = 0, phi < 0 – we can see that the trend occilates between +ve and -ve. (gray and red lines)
  • when c = 0, phi = 0 – white noise
  • when c = 0, phi > 0 – random walk (no drift) (green and orange lines)

Write your own code to generate data from an MA(1) model with theta_1=0.6 and sigma_2=1.

y <- ts(data = numeric(100))
e <- rnorm(n = 100, mean = 0, sd = 1)
for (i in 2:100){
    y[i] <- 0.6*e[i-1] + e[i]
}

Produce a time plot for the series. How does the plot change as you change theta_1?

plot(y)

There doesn’t seem to any concrete relationship between theta and the result.

Generate data from an ARMA(1,1) model with phi_1 = 0.6 and theta_1=0.6 and sigma_2=1.

y <- ts(numeric(100))
e <- rnorm(100)
for(i in 2:100)
    y[i] <- 0.6 * y[i - 1] + 0.6 * e[i - 1] + e[i]
plot(y)

Generate data from an AR(2) model with phi_1=-0.8 and phi_2=0.3 and sigma_2=1. (Note that these parameters will give a non-stationary series.)

y <- ts(numeric(100))
e <- rnorm(100)
for(i in 3:100)
    y[i] <- 0.8 * y[i - 1] + 0.3 * y[i - 2] + e[i]
plot(y)

Graph the latter two series and compare them.

Graphed above. While the ARMA(1,1) gives a stationary series, the AR(2) is clearly non-stationary.

Consider the number of women murdered each year (per 100,000 standard population) in the United States (data set wmurders).

By studying appropriate graphs of the series in R, find an appropriate ARIMA(p,d,q) model for these data.

  • The time series has trending behaviour. It doesn’t seem to have any seasonal behavior.
kpss.test(wmurders)
p-value smaller than printed p-value

    KPSS Test for Level Stationarity

data:  wmurders
KPSS Level = 1.2017, Truncation lag parameter = 1, p-value = 0.01

The KPSS test shows us that we must reject the H0: data are stationary. Differencing the time series by 1:

ggtsdisplay(diff(wmurders,lag = 1))

  • After differencing, the time series seems to be fairly stationary
  • ACF shows us a significant 2nd order component. The PACF also shows a 2nd order significant component.
kpss.test(diff(wmurders,1))

    KPSS Test for Level Stationarity

data:  diff(wmurders, 1)
KPSS Level = 0.58729, Truncation lag parameter = 1, p-value = 0.02379

Trying one more differencing scheme. Double differencing - Differencing the difference by 1:

ggtsdisplay(diff(diff(wmurders,lag = 1)))

  • PACF reduces exponentially.
  • ACF has a significant order 2, and no significant orders after that.

Thus, I can guess that an ARIMA(0, 2, 2) might fit the data well.

Should you include a constant in the model? Explain.

Yes, I think we should include a constant. With a constant value included, with a d=1 or d=2, a long term forecast can follow a line or quadratic curve. The time series seems to be downward trending, so a constant will help. If c = 0, with d = 0 or d = 1, we won’t get a downward trend. (With c = 0 and d = 2, we can still get a linear trend).

Write this model in terms of the backshift operator.

(1 - B)^2 y_t = c + (1 + theta1 x B + theta2 x B^2) x e_t

Fit the model using R and examine the residuals. Is the model satisfactory?

fit1 <- Arima(wmurders, order = c(0, 2, 2))
summary(fit1)
Series: wmurders 
ARIMA(0,2,2) 

Coefficients:
          ma1     ma2
      -1.0181  0.1470
s.e.   0.1220  0.1156

sigma^2 estimated as 0.04702:  log likelihood=6.03
AIC=-6.06   AICc=-5.57   BIC=-0.15

Training set error measures:
                     ME      RMSE       MAE        MPE     MAPE      MASE        ACF1
Training set -0.0113461 0.2088162 0.1525773 -0.2403396 4.331729 0.9382785 -0.05094066
checkresiduals(fit1)

    Ljung-Box test

data:  Residuals from ARIMA(0,2,2)
Q* = 11.764, df = 8, p-value = 0.1621

Model df: 2.   Total lags used: 10

The Ljung-Box test shows a p-val > 0.05, so there is little chance of autocorrelation in the residuals. Visually looking at the ACF plot, we can see some there is periodic elements in the residuals. The model seems adequate.

Forecast three times ahead. Check your forecasts by hand to make sure you know how they have been calculated.

fit1 %>% forecast(h = 3)
     Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
2005       2.480525 2.202620 2.758430 2.055506 2.905544
2006       2.374890 1.985422 2.764359 1.779250 2.970531
2007       2.269256 1.772305 2.766206 1.509235 3.029276

Create a plot of the series with forecasts and prediction intervals for the next three periods shown.

fit1 %>% forecast(h = 3) %>% autoplot()

Does auto.arima give the same model you have chosen? If not, which model do you think is better?

If we allow stepwise:

auto.arima(wmurders, seasonal = F, allowdrift = T)
Series: wmurders 
ARIMA(1,2,1) 

Coefficients:
          ar1      ma1
      -0.2434  -0.8261
s.e.   0.1553   0.1143

sigma^2 estimated as 0.04632:  log likelihood=6.44
AIC=-6.88   AICc=-6.39   BIC=-0.97

If we force a thorough investigation:

auto.arima(wmurders, seasonal = T, stepwise = F, allowdrift = T)
Series: wmurders 
ARIMA(0,2,3) 

Coefficients:
          ma1     ma2      ma3
      -1.0154  0.4324  -0.3217
s.e.   0.1282  0.2278   0.1737

sigma^2 estimated as 0.04475:  log likelihood=7.77
AIC=-7.54   AICc=-6.7   BIC=0.35

The ARIMA(0,2,3) has the lowest AICc value (-6.7) against my selection of ARIMA(0,2,2) with an AICc of -5.7.

Consider the quarterly number of international tourists to Australia for the period 1999–2010. (Data set austourists.)

Describe the time plot.

  • Trend component exists
  • Seasonality exists
  • Seasonality increases over time

I’m going to log transform the data

What can you learn from the ACF graph?

Very strong seasonality - 4, 8, 12, 16 seen in the components. Reducing strength of the contributions as expected.

What can you learn from the PACF graph?

Strong seasonal components at lag=4. Perhaps a strong non-seasonal component at lag=5.

Produce plots of the seasonally differenced data (1-B^4)Yt. What model do these graphs suggest?

After adjusting for seasonality, and differencing the data once to make it stationary, the ACF and PACF plots tell me a significant lag of 1 exists. It’s hard for me to pick of p=1 or q=1 based on this plot alone.

Perhaps I might choose a ARIMA(1,0,0)(0,1,1)[4] or a ARIMA(0,0,1)(0,1,1)[4]. But I can’t pick without more analysis.

Does auto.arima give the same model that you chose? If not, which model do you think is better?

auto.arima(laustourists, stepwise = F)
Series: laustourists 
ARIMA(1,0,0)(0,1,1)[4] with drift 

Coefficients:
         ar1     sma1   drift
      0.4154  -0.9043  0.0128
s.e.  0.1387   0.2711  0.0011

sigma^2 estimated as 0.004541:  log likelihood=54.52
AIC=-101.05   AICc=-100.02   BIC=-93.91

It’s similar to what I picked, and I tend to agree with it’s selection.

Write the model in terms of the backshift operator, and then without using the backshift operator.

(1 - phi1 x B)(1 - PHI1 x B^4)(1 - B^4)y_t = (1 + THETA1 x B^4)e_t

Consider the total net generation of electricity (in billion kilowatt hours) by the U.S. electric industry (monthly for the period 1985–1996). (Data set usmelec.) In general there are two peaks per year: in mid-summer and mid-winter.

Examine the 12-month moving average of this series to see what kind of trend is involved.

Do the data need transforming? If so, find a suitable transformation.

Yes, there is an increasing seasonality. Looks like an inverse sqrt will work.

BoxCox.lambda(usmelec)
[1] -0.4772402

Are the data stationary? If not, find an appropriate differencing which yields stationary data.

The data are not stationary. A difference of 1 is needed to make it stationary according to the KPSS test.

diff(lusmelec,1) %>%  kpss.test()
p-value greater than printed p-value

    KPSS Test for Level Stationarity

data:  .
KPSS Level = 0.022613, Truncation lag parameter = 4, p-value = 0.1

Visually, the signal looks stationary.

Zooming in a bit more, I think I can see seasonality which a diff=1 hasn’t got rid of.

If we look at the ACF, PACF plots, we can see two patterns: * a seasonal pattern at 12, 24, 36 * another seasonal pattern at 3, 6, 9, …

Looks like we have a complex dual-seasonal pattern even after differencing.

The PACF plot shows lags at 12, 24, 36 which are seasonal lags. Perhaps an AR(3) term for seasonal is needed. For non-seasonal component, considering the drop in ACF values, the most significant PACF value is for lag = 1. Perhaps an ARIMA(1,0,0)(0,1,3)[12] is a good guess.

Identify a couple of ARIMA models that might be useful in describing the time series. Which of your models is the best according to their AIC values?

It looks like ARIMA(1,0,1)(1,1,1)[12] has the lowest AIC of -4240.98, after some investigation.

Arima(lusmelec, order = c(1,0,0), seasonal = list(order=c(0,1,3), period=12))
Series: lusmelec 
ARIMA(1,0,0)(0,1,3)[12] 

Coefficients:
         ar1     sma1     sma2    sma3
      0.9843  -0.8726  -0.0957  0.1128
s.e.  0.0106   0.0512   0.0581  0.0502

sigma^2 estimated as 4.358e-06:  log likelihood=2098.67
AIC=-4187.35   AICc=-4187.21   BIC=-4166.89
Arima(lusmelec, order = c(1,0,0), seasonal = list(order=c(0,1,2), period=12))
Series: lusmelec 
ARIMA(1,0,0)(0,1,2)[12] 

Coefficients:
         ar1     sma1     sma2
      0.9858  -0.8526  -0.0223
s.e.  0.0100   0.0551   0.0531

sigma^2 estimated as 4.398e-06:  log likelihood=2096.22
AIC=-4184.43   AICc=-4184.34   BIC=-4168.07
Arima(lusmelec, order = c(1,0,0), seasonal = list(order=c(1,1,2), period=12))
Series: lusmelec 
ARIMA(1,0,0)(1,1,2)[12] 

Coefficients:
         ar1     sar1     sma1     sma2
      0.9864  -0.4831  -0.3578  -0.4591
s.e.  0.0099   0.3726   0.3533   0.3042

sigma^2 estimated as 4.401e-06:  log likelihood=2096.53
AIC=-4183.06   AICc=-4182.92   BIC=-4162.6
Arima(lusmelec, order = c(1,0,1), seasonal = list(order=c(0,1,3), period=12))
Series: lusmelec 
ARIMA(1,0,1)(0,1,3)[12] 

Coefficients:
         ar1      ma1     sma1     sma2    sma3
      0.9965  -0.5114  -0.8071  -0.1279  0.0895
s.e.  0.0037   0.0645   0.0512   0.0577  0.0508

sigma^2 estimated as 3.881e-06:  log likelihood=2126.49
AIC=-4240.98   AICc=-4240.79   BIC=-4216.44
Arima(lusmelec, order = c(1,0,1), seasonal = list(order=c(1,1,1), period=12))
Series: lusmelec 
ARIMA(1,0,1)(1,1,1)[12] 

Coefficients:
         ar1      ma1    sar1     sma1
      0.9971  -0.5191  0.0702  -0.8720
s.e.  0.0033   0.0645  0.0568   0.0309

sigma^2 estimated as 3.903e-06:  log likelihood=2124.75
AIC=-4239.51   AICc=-4239.37   BIC=-4219.05

Estimate the parameters of your best model and do diagnostic testing on the residuals. Do the residuals resemble white noise? If not, try to find another ARIMA model which fits better.

fit_selected <- Arima(lusmelec, order = c(1,0,1), seasonal = list(order=c(1,1,3), period=12))
fit_selected %>% summary()
Series: lusmelec 
ARIMA(1,0,1)(1,1,3)[12] 

Coefficients:
         ar1      ma1    sar1     sma1    sma2    sma3
      0.9959  -0.5069  0.3740  -1.1789  0.1630  0.1226
s.e.  0.0042   0.0651  0.3026   0.3007  0.2565  0.0506

sigma^2 estimated as 3.881e-06:  log likelihood=2127.04
AIC=-4240.07   AICc=-4239.81   BIC=-4211.43

Training set error measures:
                       ME        RMSE         MAE        MPE       MAPE      MASE      ACF1
Training set 0.0001088471 0.001930585 0.001511984 0.00564225 0.07789407 0.5754233 0.1284949
fit_selected %>% checkresiduals()

    Ljung-Box test

data:  Residuals from ARIMA(1,0,1)(1,1,3)[12]
Q* = 54.46, df = 18, p-value = 1.556e-05

Model df: 6.   Total lags used: 24

Residuals show a left skew and the ACF plot shows significant lags at 1,2,14 etc. Trying out another model: an ARIMA(2,0,2)(1,1,3)[12] improved the AIC to -4264.

fit_selected <- Arima(lusmelec, order = c(2,0,2), seasonal = list(order=c(1,1,3), period=12))
fit_selected %>% summary()
Series: lusmelec 
ARIMA(2,0,2)(1,1,3)[12] 

Coefficients:
         ar1      ar2      ma1      ma2    sar1     sma1    sma2    sma3
      1.3027  -0.3035  -0.7186  -0.0789  0.4392  -1.2694  0.2476  0.1115
s.e.  0.1826   0.1822   0.1872   0.1259  0.3036   0.3025  0.2690  0.0489

sigma^2 estimated as 3.656e-06:  log likelihood=2141.09
AIC=-4264.18   AICc=-4263.77   BIC=-4227.36

Training set error measures:
                       ME        RMSE         MAE         MPE       MAPE      MASE       ACF1
Training set 3.731734e-05 0.001869441 0.001448626 0.001974489 0.07462978 0.5513111 0.02330224
fit_selected %>% checkresiduals()

    Ljung-Box test

data:  Residuals from ARIMA(2,0,2)(1,1,3)[12]
Q* = 29.996, df = 16, p-value = 0.01802

Model df: 8.   Total lags used: 24

What does an auto.arima say? ARIMA(1,1,1)(2,1,1)[12]… but the AICc is worse than the model I chose.

fit_selected <- auto.arima(lusmelec, stepwise = F, approximation = F)
fit_selected <- auto.arima(lusmelec, stepwise = F, approximation = F)
fit_selected %>% summary()
Series: lusmelec 
ARIMA(1,1,1)(2,1,1)[12] 

Coefficients:
         ar1      ma1    sar1     sar2     sma1
      0.4002  -0.8295  0.0269  -0.1016  -0.8485
s.e.  0.0652   0.0385  0.0579   0.0553   0.0366

sigma^2 estimated as 3.653e-06:  log likelihood=2135.33
AIC=-4258.65   AICc=-4258.46   BIC=-4234.12

Training set error measures:
                        ME        RMSE         MAE          MPE       MAPE      MASE        ACF1
Training set -9.026359e-05 0.001873122 0.001423716 -0.004660559 0.07332098 0.5418307 0.007791069
fit_selected %>% checkresiduals()

    Ljung-Box test

data:  Residuals from ARIMA(1,1,1)(2,1,1)[12]
Q* = 27.628, df = 19, p-value = 0.09086

Model df: 5.   Total lags used: 24

Forecast the next 15 years of generation of electricity by the U.S. electric industry. Get the latest figures from http://data.is/zgRWCO to check on the accuracy of your forecasts.

The forecasted model works quite well!! The green line shows the real data for the past few years, while the red line is my forecasts. I am quite pleased.

How many years of forecasts do you think are sufficiently accurate to be usable?

Probably a year or two years out. Because forecasting assumes the underlying data generating process remains unchanged. If this changes, then the model is useless.

LS0tCnRpdGxlOiAiSFcgMiIKYXV0aG9yOiAiUmFodWwgU2FuZ29sZSIKZGF0ZTogQXByaWwgMjksIDIwMTgKZm9udHNpemU6IDExcHQKb3V0cHV0OgogIHBkZl9kb2N1bWVudDogCiAgICBkZl9wcmludDoga2FibGUKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDIKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQpgYGB7ciBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KGZwcCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KG1hZ3JpdHRyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGZtYSkKbGlicmFyeShtYWdyaXR0cikKYGBgCgojIFNlY3Rpb24gNy44CiMjRGF0YSBzZXQgYm9va3MgY29udGFpbnMgdGhlIGRhaWx5IHNhbGVzIG9mIHBhcGVyYmFjayBhbmQgaGFyZGNvdmVyIGJvb2tzIGF0IHRoZSBzYW1lIHN0b3JlLiBUaGUgdGFzayBpcyB0byBmb3JlY2FzdCB0aGUgbmV4dCBmb3VyIGRheXPigJkgc2FsZXMgZm9yIHBhcGVyYmFjayBhbmQgaGFyZGNvdmVyIGJvb2tzCiMjI1Bsb3QgdGhlIHNlcmllcyBhbmQgZGlzY3VzcyB0aGUgbWFpbiBmZWF0dXJlcyBvZiB0aGUgZGF0YS4KCkJvdGggdGhlIHRpbWUgc2VyaWVzIHNob3cgYSBsaW5lYXIgdXB3YXJkIHRyZW5kLiBWaXN1YWxseSwgdGhlIGBQYXBlcmJhY2tgIHNlZW1zIHRvIGhhdmUgc29tZSByZWd1bGFyIHBhdHRlcnMgKHNlYXNvbmFsaXR5KSBhcHByb3hpbWF0ZWx5IGVxdWFsIHRvIDMgZGF5cy4gCgpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CmF1dG9wbG90KGJvb2tzLCBmYWNldHMgPSBUKQpgYGAKClRoZXJlIGRvZXNuJ3Qgc2VlbSB0byBiZSBhbnkgbGluZWFyIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHR3byBzZXJpZXMuCgpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CmxhdHRpY2U6Onh5cGxvdChQYXBlcmJhY2t+SGFyZGNvdmVyLCBhcy5kYXRhLmZyYW1lKGJvb2tzKSwgdHlwZT1jKCdwJywnc21vb3RoJykpCmBgYAoKVGhlIFBBQ0YgcGxvdHMgZG8gc2hvdyB0aGF0IGZvciBgUGFwZXJiYWNrYCwgdGhlcmUgaXMgYSAzLW9yZGVyIGF1dG9jb3JyZWxhdGlvbiBpbiB0aGUgc2lnbmFsIHdoaWNoIGlzIHNpZ25pZmljYW50LiBGb3IgYEhhcmRjb3ZlcmAsIHRoZXJlIGlzIGEgMXN0IGFuZCAybmQgb3JkZXIgc2lnbmlmaWNhbmNlLiAKCmBgYHtyIG91dC53aWR0aD0gJzcwJScsIGZpZy5hbGlnbj0nY2VudGVyJ30KZ2dQYWNmKGJvb2tzWywnUGFwZXJiYWNrJ10pCmdnUGFjZihib29rc1ssJ0hhcmRjb3ZlciddKQpgYGAKCiMjI1VzZSBzaW1wbGUgZXhwb25lbnRpYWwgc21vb3RoaW5nIHdpdGggdGhlIHNlcyBmdW5jdGlvbiAoc2V0dGluZyBpbml0aWFsPSJzaW1wbGUiKSBhbmQgZXhwbG9yZSBkaWZmZXJlbnQgdmFsdWVzIG9mIGFscGhhIGZvciB0aGUgcGFwZXJiYWNrIHNlcmllcy4gCgpIZXJlIGFyZSB0aHJlZSBzZXR0aW5ncyAtIGE9MC45LCBhPTAuNSwgYW5kIGE9MC4wMS4gQXMgYGFscGhhYCBpbmNyZWFzZXMsIHNvIGRvZXMgdGhlIHVuY2VydGFpbnR5IG9mIHRoZSBwcmVkaWN0aW9uIHNpbmNlIGBzZXNgIHdpbGwgbG9vayBiYWNrIGZ1cnRoZXIgaW4gdGltZS4gVGhvdWdoIGF0IGFscGhhID0gMC4wMSwgdGhlIHBvaW50IGVzdGltYXRlIHNlZW1zIHF1aXRlIGxvdy4KYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQpzZXMoYm9va3NbLCdQYXBlcmJhY2snXSwgaW5pdGlhbCA9ICdzaW1wbGUnLCBhbHBoYSA9IC45KSAgJT4lIGZvcmVjYXN0KCkgJT4lIGF1dG9wbG90KCkKc2VzKGJvb2tzWywnUGFwZXJiYWNrJ10sIGluaXRpYWwgPSAnc2ltcGxlJywgYWxwaGEgPSAuNSkgICU+JSBmb3JlY2FzdCgpICU+JSBhdXRvcGxvdCgpCnNlcyhib29rc1ssJ1BhcGVyYmFjayddLCBpbml0aWFsID0gJ3NpbXBsZScsIGFscGhhID0gLjAxKSAlPiUgZm9yZWNhc3QoKSAlPiUgYXV0b3Bsb3QoKQpgYGAKCiMjI1JlY29yZCB0aGUgd2l0aGluLXNhbXBsZSBTU0UgZm9yIHRoZSBvbmUtc3RlcCBmb3JlY2FzdHMuIFBsb3QgU1NFIGFnYWluc3QgYWxwaGEgYW5kIGZpbmQgd2hpY2ggdmFsdWUgb2YgYWxwaGEgd29ya3MgYmVzdC4gV2hhdCBpcyB0aGUgZWZmZWN0IG9mIGFscGhhIG9uIHRoZSBmb3JlY2FzdHM/CgpXZSBjYW4gc2VlIGEgdHlwaWNhbCBjdXJ2ZSBhcyBzZWVuIGR1cmluZyBwYXJhbWV0ZXIgdHVuaW5nLiBUaGUgU1NFIGlzIG1pbmltdW0gYXQgYW4gYWxwaGEgdmFsdWUgb2YgYWJvdXQgMC4yLgoKYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQpzc2VfbGlzdCA8LSBjKCkKYV9saXN0IDwtIHNlcSgwLjAwMSwgMC45OTksIGxlbmd0aC5vdXQgPSAyMCkKZm9yIChhX3NlbCBpbiBhX2xpc3QpIHsKICAgIHNlcyhib29rc1ssJ1BhcGVyYmFjayddLCBpbml0aWFsID0gJ3NpbXBsZScsIGFscGhhID0gYV9zZWwpJG1vZGVsJFNTRSAtPiBzc2UKICAgIHNzZV9saXN0IDwtIGMoc3NlX2xpc3QsIHNzZSkKfQpwbG90KGFfbGlzdCwgc3NlX2xpc3QsIHR5cGU9J2InKQpwb2ludHMoYV9saXN0W3doaWNoLm1pbihzc2VfbGlzdCldLCBzc2VfbGlzdFt3aGljaC5taW4oc3NlX2xpc3QpXSwgY29sPSdyZWQnLCBwY2g9MjApCgpgYGAKCiMjI05vdyBsZXQgc2VzIHNlbGVjdCB0aGUgb3B0aW1hbCB2YWx1ZSBvZiBhbHBoYS4gVXNlIHRoaXMgdmFsdWUgdG8gZ2VuZXJhdGUgZm9yZWNhc3RzIGZvciB0aGUgbmV4dCBmb3VyIGRheXMuIENvbXBhcmUgeW91ciByZXN1bHRzIHdpdGggMi4KCkFscGhhIHNlbGVjdGVkIGlzIDAuMjE1LiAgVGhlIHBvaW50IGVzdGltYXRlIGZvciB0aGlzIGZvcmVjYXN0IHNlZW0gYmV0dGVyIHRoYW4gdGhlICMyIHJlc3VsdHMuIFByZWRpY3Rpb24gaW50ZXJ2YWwgd2lkdGhzIGFyZSBhYm91dCBzaW1pbGFyLiBSTVNFIGlzIHNtYWxsZXIgZm9yIHRoZSBhdXRvLWFscGhhIHNlbGVjdGlvbi4KCmBgYHtyIG91dC53aWR0aD0gJzcwJScsIGZpZy5hbGlnbj0nY2VudGVyJ30Kc2VzKGJvb2tzWywnUGFwZXJiYWNrJ10sIGluaXRpYWwgPSAnc2ltcGxlJywgYWxwaGEgPSBOVUxMKSRtb2RlbCRwYXJbJ2FscGhhJ10Kc2VzKGJvb2tzWywnUGFwZXJiYWNrJ10sIGluaXRpYWwgPSAnc2ltcGxlJywgYWxwaGEgPSBOVUxMKSU+JSBmb3JlY2FzdChoPTQpJT4lIGF1dG9wbG90KCkKYGBgCmBgYHtyfQpiaW5kX3Jvd3Moc2VzKGJvb2tzWywnUGFwZXJiYWNrJ10sIGluaXRpYWwgPSAnc2ltcGxlJywgYWxwaGEgPSBOVUxMKSAlPiUgCiAgICAgICAgICAgICAgZm9yZWNhc3QoaD00KSAlPiUgYWNjdXJhY3koKSAlPiUgc3dlZXA6OnN3X3RpZHkoKSwKICAgICAgICAgIHNlcyhib29rc1ssJ1BhcGVyYmFjayddLCBpbml0aWFsID0gJ3NpbXBsZScsIGFscGhhID0gMC4wMSkgJT4lIAogICAgICAgICAgICAgIGZvcmVjYXN0KGg9NCkgJT4lIGFjY3VyYWN5KCkgJT4lIHN3ZWVwOjpzd190aWR5KCkpICU+JSAKICAgIGtuaXRyOjprYWJsZShmb3JtYXQgPSAnbGF0ZXgnLGNhcHRpb24gPSAnMXN0IGxpbmU6IEF1dG8tYWxwaGEuIDJuZCBsaW5lOiBhPTAuMDEnKQpgYGAKCiMjI1JlcGVhdCBidXQgd2l0aCBpbml0aWFsPSJvcHRpbWFsIi4gSG93IG11Y2ggZGlmZmVyZW5jZSBkb2VzIGFuIG9wdGltYWwgaW5pdGlhbCBsZXZlbCBtYWtlPwoKU2V0dGluZyB0aGUgaW5pdGlhbCB0byBvcHRpbWFsIHJlZHVjZXMgUk1TRSBieSB+MSB1bml0LCBhbmQgTUFTRSBieSB+MC4wMiB1bml0cy4KCmBgYHtyfQpzZXMoYm9va3NbLCdQYXBlcmJhY2snXSwgaW5pdGlhbCA9ICdvcHRpbWFsJywgYWxwaGEgPSBOVUxMKSAtPiBmaXRfb3B0aW1hbApzZXMoYm9va3NbLCdQYXBlcmJhY2snXSwgaW5pdGlhbCA9ICdzaW1wbGUnLCBhbHBoYSA9IE5VTEwpIC0+IGZpdF9zaW1wbGUKYmluZF9yb3dzKGZpdF9vcHRpbWFsICU+JSBmb3JlY2FzdChoPTQpICU+JSBhY2N1cmFjeSgpICU+JSBzd2VlcDo6c3dfdGlkeSgpLCAKICAgICAgICAgIGZpdF9zaW1wbGUgJT4lIGZvcmVjYXN0KGg9NCkgJT4lIGFjY3VyYWN5KCkgJT4lIHN3ZWVwOjpzd190aWR5KCkpICU+JSAKICAgIGtuaXRyOjprYWJsZShmb3JtYXQgPSAnbGF0ZXgnLAogICAgICAgICAgICAgICAgIGNhcHRpb24gPSAnMXN0IGxpbmU6IE9wdGltYWwgaW5pdGlhbC4gMm5kIGxpbmU6IFNpbXBsZSBpbml0aWFsJykKYGBgCgojIyNSZXBlYXQgc3RlcHMgKGIp4oCTKGQpIHdpdGggdGhlIGhhcmRjb3ZlciBzZXJpZXMuCgojIyMjVXNlIHNpbXBsZSBleHBvbmVudGlhbCBzbW9vdGhpbmcgd2l0aCB0aGUgc2VzIGZ1bmN0aW9uIChzZXR0aW5nIGluaXRpYWw9InNpbXBsZSIpIGFuZCBleHBsb3JlIGRpZmZlcmVudCB2YWx1ZXMgb2YgYWxwaGEgZm9yIHRoZSBwYXBlcmJhY2sgc2VyaWVzLiAKClRoaXMgc2VyaWVzIGRvZXMgYmV0dGVyIHdpdGggYSBoaWdoZXIgdmFsdWUgb2YgYWxwaGEuCgpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CnNlcyhib29rc1ssJ0hhcmRjb3ZlciddLCBpbml0aWFsID0gJ3NpbXBsZScsIGFscGhhID0gLjkpICAlPiUgZm9yZWNhc3QoKSAlPiUgYXV0b3Bsb3QoKQpzZXMoYm9va3NbLCdIYXJkY292ZXInXSwgaW5pdGlhbCA9ICdzaW1wbGUnLCBhbHBoYSA9IC41KSAgJT4lIGZvcmVjYXN0KCkgJT4lIGF1dG9wbG90KCkKc2VzKGJvb2tzWywnSGFyZGNvdmVyJ10sIGluaXRpYWwgPSAnc2ltcGxlJywgYWxwaGEgPSAuMDEpICU+JSBmb3JlY2FzdCgpICU+JSBhdXRvcGxvdCgpCmBgYCAKCiMjIyNSZWNvcmQgdGhlIHdpdGhpbi1zYW1wbGUgU1NFIGZvciB0aGUgb25lLXN0ZXAgZm9yZWNhc3RzLiBQbG90IFNTRSBhZ2FpbnN0IGFscGhhIGFuZCBmaW5kIHdoaWNoIHZhbHVlIG9mIGFscGhhIHdvcmtzIGJlc3QuIFdoYXQgaXMgdGhlIGVmZmVjdCBvZiBhbHBoYSBvbiB0aGUgZm9yZWNhc3RzPwoKV2UgY2FuIHNlZSBhIHR5cGljYWwgY3VydmUgYXMgc2VlbiBkdXJpbmcgcGFyYW1ldGVyIHR1bmluZy4gVGhlIFNTRSBpcyBtaW5pbXVtIGF0IGFuIGFscGhhIHZhbHVlIG9mIGFib3V0IDAuMzUuCgpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CnNzZV9saXN0IDwtIGMoKQphX2xpc3QgPC0gc2VxKDAuMDAxLCAwLjk5OSwgbGVuZ3RoLm91dCA9IDIwKQpmb3IgKGFfc2VsIGluIGFfbGlzdCkgewogICAgc2VzKGJvb2tzWywnSGFyZGNvdmVyJ10sIGluaXRpYWwgPSAnc2ltcGxlJywgYWxwaGEgPSBhX3NlbCkkbW9kZWwkU1NFIC0+IHNzZQogICAgc3NlX2xpc3QgPC0gYyhzc2VfbGlzdCwgc3NlKQp9CnBsb3QoYV9saXN0LCBzc2VfbGlzdCwgdHlwZT0nYicpCnBvaW50cyhhX2xpc3Rbd2hpY2gubWluKHNzZV9saXN0KV0sIHNzZV9saXN0W3doaWNoLm1pbihzc2VfbGlzdCldLCBjb2w9J3JlZCcsIHBjaD0yMCkKYGBgCgojIyMjTm93IGxldCBzZXMgc2VsZWN0IHRoZSBvcHRpbWFsIHZhbHVlIG9mIGFscGhhLiBVc2UgdGhpcyB2YWx1ZSB0byBnZW5lcmF0ZSBmb3JlY2FzdHMgZm9yIHRoZSBuZXh0IGZvdXIgZGF5cy4gQ29tcGFyZSB5b3VyIHJlc3VsdHMgd2l0aCAyLgoKQWxwaGEgc2VsZWN0ZWQgaXMgMC4zNDcuIFRoZSBwb2ludCBlc3RpbWF0ZSBmb3IgdGhpcyBmb3JlY2FzdCBzZWVtIGJldHRlciB0aGFuIHByZXZpb3VzIHJlc3VsdHMuIFByZWRpY3Rpb24gaW50ZXJ2YWwgd2lkdGhzIGFyZSBhYm91dCBzaW1pbGFyLiBSTVNFIGlzIHNtYWxsZXIgZm9yIHRoZSBhdXRvLWFscGhhIHNlbGVjdGlvbi4KCmBgYHtyIG91dC53aWR0aD0gJzcwJScsIGZpZy5hbGlnbj0nY2VudGVyJ30Kc2VzKGJvb2tzWywnSGFyZGNvdmVyJ10sIGluaXRpYWwgPSAnc2ltcGxlJywgYWxwaGEgPSBOVUxMKSRtb2RlbCRwYXJbJ2FscGhhJ10Kc2VzKGJvb2tzWywnSGFyZGNvdmVyJ10sIGluaXRpYWwgPSAnc2ltcGxlJywgYWxwaGEgPSBOVUxMKSU+JSBmb3JlY2FzdChoPTQpJT4lIGF1dG9wbG90KCkKYGBgCmBgYHtyfQpiaW5kX3Jvd3Moc2VzKGJvb2tzWywnSGFyZGNvdmVyJ10sIGluaXRpYWwgPSAnc2ltcGxlJywgYWxwaGEgPSBOVUxMKSAlPiUgCiAgICAgICAgICAgICAgZm9yZWNhc3QoaD00KSAlPiUgYWNjdXJhY3koKSAlPiUgc3dlZXA6OnN3X3RpZHkoKSwKICAgICAgICAgIHNlcyhib29rc1ssJ0hhcmRjb3ZlciddLCBpbml0aWFsID0gJ3NpbXBsZScsIGFscGhhID0gMC45KSAlPiUgCiAgICAgICAgICAgICAgZm9yZWNhc3QoaD00KSAlPiUgYWNjdXJhY3koKSAlPiUgc3dlZXA6OnN3X3RpZHkoKSkgJT4lIAogICAga25pdHI6OmthYmxlKGZvcm1hdCA9ICdsYXRleCcsY2FwdGlvbiA9ICcxc3QgbGluZTogQXV0by1hbHBoYS4gMm5kIGxpbmU6IGE9MC4wMScpCmBgYAoKIyMjI1JlcGVhdCBidXQgd2l0aCBpbml0aWFsPSJvcHRpbWFsIi4gSG93IG11Y2ggZGlmZmVyZW5jZSBkb2VzIGFuIG9wdGltYWwgaW5pdGlhbCBsZXZlbCBtYWtlPwoKU2V0dGluZyB0aGUgaW5pdGlhbCB0byBvcHRpbWFsIHJlZHVjZXMgUk1TRSBieSB+MiB1bml0cywgYnV0IE1BU0UgaW5jcmVhc2VkIGJ5IH4wLjAxIHVuaXRzLgoKYGBge3J9CnNlcyhib29rc1ssJ0hhcmRjb3ZlciddLCBpbml0aWFsID0gJ29wdGltYWwnLCBhbHBoYSA9IE5VTEwpIC0+IGZpdF9vcHRpbWFsCnNlcyhib29rc1ssJ0hhcmRjb3ZlciddLCBpbml0aWFsID0gJ3NpbXBsZScsIGFscGhhID0gTlVMTCkgLT4gZml0X3NpbXBsZQpiaW5kX3Jvd3MoZml0X29wdGltYWwgJT4lIGZvcmVjYXN0KGg9NCkgJT4lIGFjY3VyYWN5KCkgJT4lIHN3ZWVwOjpzd190aWR5KCksIAogICAgICAgICAgZml0X3NpbXBsZSAlPiUgZm9yZWNhc3QoaD00KSAlPiUgYWNjdXJhY3koKSAlPiUgc3dlZXA6OnN3X3RpZHkoKSkgJT4lIAogICAga25pdHI6OmthYmxlKGZvcm1hdCA9ICdsYXRleCcsCiAgICAgICAgICAgICAgICAgY2FwdGlvbiA9ICcxc3QgbGluZTogT3B0aW1hbCBpbml0aWFsLiAybmQgbGluZTogU2ltcGxlIGluaXRpYWwnKQpgYGAKCiMjQXBwbHkgSG9sdOKAmXMgbGluZWFyIG1ldGhvZCB0byB0aGUgcGFwZXJiYWNrIGFuZCBoYXJkYmFjayBzZXJpZXMgYW5kIGNvbXB1dGUgZm91ci1kYXkgZm9yZWNhc3RzIGluIGVhY2ggY2FzZS4KCmBgYHtyIG91dC53aWR0aD0gJzcwJScsIGZpZy5hbGlnbj0nY2VudGVyJ30KaG9sdChib29rc1ssJ1BhcGVyYmFjayddKSAlPiUgZm9yZWNhc3QoaD00KSAlPiUgYXV0b3Bsb3QoKQpob2x0KGJvb2tzWywnSGFyZGNvdmVyJ10pICU+JSBmb3JlY2FzdChoPTQpICU+JSBhdXRvcGxvdCgpCmBgYAoKIyMjQ29tcGFyZSB0aGUgU1NFIG1lYXN1cmVzIG9mIEhvbHTigJlzIG1ldGhvZCBmb3IgdGhlIHR3byBzZXJpZXMgdG8gdGhvc2Ugb2Ygc2ltcGxlIGV4cG9uZW50aWFsIHNtb290aGluZyBpbiB0aGUgcHJldmlvdXMgcXVlc3Rpb24uIERpc2N1c3MgdGhlIG1lcml0cyBvZiB0aGUgdHdvIGZvcmVjYXN0aW5nIG1ldGhvZHMgZm9yIHRoZXNlIGRhdGEgc2V0cy4KClRoZSBgaG9sdGAgbWV0aG9kIGRlZmluaXRlbHkgaGFzIGEgbG93ZXIgU1NFIHRoYW4gdGhlIGBzc2VgIG1ldGhvZCBzaW5jZSBgaG9sdGAgY2FuIGFjY291bnQgZm9yIHRoZSB1cHdhcmQgdHJlbmQgaW4gYm90aCB0aGUgdGltZXNlcmllcy4gYHNlc2AgdW5mb3J0dW5hdGVseSwgY2Fubm90IGFjY291bnQgZm9yIHRoaXMgdHJlbmQuCgpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CnNlcyhib29rc1ssJ1BhcGVyYmFjayddKSAlPiUgcmVzaWR1YWxzKCkgJT4lIC5eMiAlPiUgc3VtKCkKc2VzKGJvb2tzWywnSGFyZGNvdmVyJ10pICU+JSByZXNpZHVhbHMoKSAlPiUgLl4yICU+JSBzdW0oKQoKaG9sdChib29rc1ssJ1BhcGVyYmFjayddKSAlPiUgcmVzaWR1YWxzKCkgJT4lIC5eMiAlPiUgc3VtKCkKaG9sdChib29rc1ssJ0hhcmRjb3ZlciddKSAlPiUgcmVzaWR1YWxzKCkgJT4lIC5eMiAlPiUgc3VtKCkKYGBgCgojIyNDb21wYXJlIHRoZSBmb3JlY2FzdHMgZm9yIHRoZSB0d28gc2VyaWVzIHVzaW5nIGJvdGggbWV0aG9kcy4gV2hpY2ggZG8geW91IHRoaW5rIGlzIGJlc3Q/CgpBZ2FpbiwgdGhlIGZvcmVjYXN0cyBjcmVhdGVkIGJ5IGBob2x0YCBoYXZlIHRoZSByaWdodCB1cHdhcmQgdHJlbmQgYXMgd291bGQgYmUgZXhwZWN0ZWQgZnJvbSB0aGUgCgojIyNDYWxjdWxhdGUgYSA5NSUgcHJlZGljdGlvbiBpbnRlcnZhbCBmb3IgdGhlIGZpcnN0IGZvcmVjYXN0IGZvciBlYWNoIHNlcmllcyB1c2luZyBib3RoIG1ldGhvZHMsIGFzc3VtaW5nIG5vcm1hbCBlcnJvcnMuIENvbXBhcmUgeW91ciBmb3JlY2FzdHMgd2l0aCB0aG9zZSBwcm9kdWNlZCBieSBSLgoKVXNpbmcgdGhlIGBmb3JlY2FzdGAgZnVuY3Rpb246CmBgYHtyfQpob2x0KGJvb2tzWywnUGFwZXJiYWNrJ10pICU+JSBmb3JlY2FzdChoPTEpCmhvbHQoYm9va3NbLCdIYXJkY292ZXInXSkgJT4lIGZvcmVjYXN0KGg9MSkKYGBgCgojI0ZvciB0aGlzIGV4ZXJjaXNlLCB1c2UgdGhlIHF1YXJ0ZXJseSBVSyBwYXNzZW5nZXIgdmVoaWNsZSBwcm9kdWN0aW9uIGRhdGEgZnJvbSAxOTc3OjEtLTIwMDU6MSAoZGF0YSBzZXQgdWtjYXJzKS4KCiMjI1Bsb3QgdGhlIGRhdGEgYW5kIGRlc2NyaWJlIHRoZSBtYWluIGZlYXR1cmVzIG9mIHRoZSBzZXJpZXMuCgoqIEJldHdlZW4gMTk3NyBhbmQgMTk4MCB0aGVyZSBpcyBhIGRvd253YXJkIHRyZW5kCiogRnJvbSAxOTgwIHRvIDIwMDAsIHRocmUgaXMgYSBzdGVhZHkgbGluZWFyIGluY3JlYXNpbmcgdHJlbmQKKiBTb21ldGhpbmcgaGFwcGVucyBpbiAyMDAwIHdoaWNoIGNhdXNlcyBhIHNoYXJwIGRlY2xpbmUgZm9yIGEgeWVhciBhbmQgcGlja3MgYmFjayB1cAoqIFRoZXJlIGlzIGEgcXVhcnRlcmx5IHNlYXNvbmFsaXR5IHdlIGNhbiBzZWUKCmBgYHtyIG91dC53aWR0aD0gJzcwJScsIGZpZy5hbGlnbj0nY2VudGVyJ30KYXV0b3Bsb3QodWtjYXJzKQpgYGAKCiMjI0RlY29tcG9zZSB0aGUgc2VyaWVzIHVzaW5nIFNUTCBhbmQgb2J0YWluIHRoZSBzZWFzb25hbGx5IGFkanVzdGVkIGRhdGEuCmBgYHtyIG91dC53aWR0aD0gJzcwJScsIGZpZy5hbGlnbj0nY2VudGVyJ30Kc3RsX2ZpdCA8LSBzdGwodWtjYXJzLCBzLndpbmRvdyA9ICdwZXJpb2RpYycpCmF1dG9wbG90KHN0bF9maXQpCnNhZGp1c3RlZCA8LSBzZWFzYWRqKHN0bF9maXQpCmBgYAoKIyMjRm9yZWNhc3QgdGhlIG5leHQgdHdvIHllYXJzIG9mIHRoZSBzZXJpZXMgdXNpbmcgYW4gYWRkaXRpdmUgZGFtcGVkIHRyZW5kIG1ldGhvZCBhcHBsaWVkIHRvIHRoZSBzZWFzb25hbGx5IGFkanVzdGVkIGRhdGEuIFRoZW4gcmVzZWFzb25hbGl6ZSB0aGUgZm9yZWNhc3RzLiBSZWNvcmQgdGhlIHBhcmFtZXRlcnMgb2YgdGhlIG1ldGhvZCBhbmQgcmVwb3J0IHRoZSBSTVNFIG9mIHRoZSBvbmUtc3RlcCBmb3JlY2FzdHMgZnJvbSB5b3VyIG1ldGhvZC4KYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQpob2x0Zml0IDwtIGhvbHQoc2FkanVzdGVkLCBoID0gOCwgZGFtcGVkID0gVCwgZXhwb25lbnRpYWwgPSBGKQpzZWFzb25hbGFkanVzdG1lbnRzIDwtIHVrY2Fycy1zYWRqdXN0ZWQKaG9sdGZpdF93X3NlYXNvbmFsIDwtIGhvbHRmaXQkbWVhbiArIHNlYXNvbmFsYWRqdXN0bWVudHNbMjo5XQpob2x0Zml0X3dfc2Vhc29uYWwKYGBgClBhcmFtZXRlcnMgb2YgdGhlIGZpdCBhcmUgaGVyZS4gUk1TRSBpcyAyNS4xNTk4Ni4KYGBge3J9CnN1bW1hcnkoaG9sdGZpdCkKYGBgCgojIyNGb3JlY2FzdCB0aGUgbmV4dCB0d28geWVhcnMgb2YgdGhlIHNlcmllcyB1c2luZyBIb2x0J3MgbGluZWFyIG1ldGhvZCBhcHBsaWVkIHRvIHRoZSBzZWFzb25hbGx5IGFkanVzdGVkIGRhdGEuIFRoZW4gcmVzZWFzb25hbGl6ZSB0aGUgZm9yZWNhc3RzLiBSZWNvcmQgdGhlIHBhcmFtZXRlcnMgb2YgdGhlIG1ldGhvZCBhbmQgcmVwb3J0IHRoZSBSTVNFIG9mIG9mIHRoZSBvbmUtc3RlcCBmb3JlY2FzdHMgZnJvbSB5b3VyIG1ldGhvZC4KYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQpob2x0Zml0X2FkZGl0aXZlIDwtIGhvbHQoc2FkanVzdGVkLCBoID0gOCwgZGFtcGVkID0gRiwgZXhwb25lbnRpYWwgPSBGKQpzZWFzb25hbGFkanVzdG1lbnRzIDwtIHVrY2Fycy1zYWRqdXN0ZWQKaG9sdGZpdF9hZGRpdGl2ZV93X3NlYXNvbmFsIDwtIGhvbHRmaXRfYWRkaXRpdmUkbWVhbiArIHNlYXNvbmFsYWRqdXN0bWVudHNbMjo5XQpob2x0Zml0X2FkZGl0aXZlX3dfc2Vhc29uYWwKYGBgClBhcmFtZXRlcnMgb2YgdGhlIGZpdCBhcmUgaGVyZS4gUk1TRSBpcyAyNS4yNi4KYGBge3J9CnN1bW1hcnkoaG9sdGZpdF9hZGRpdGl2ZSkKYGBgCgojIyNOb3cgdXNlIGV0cygpIHRvIGNob29zZSBhIHNlYXNvbmFsIG1vZGVsIGZvciB0aGUgZGF0YS4KCkVUUyBzZWxlY3RzIGFuIEEtQWQtQSBtb2RlbCAtIEFkZGl0aXZlIGVycm9yLCBBZGRpdGl2ZSBkYW1wZWQgc2Vhc29uYWwgYW5kIEFkZGl0aXZlIHRyZW5kIGNvbXBvbmVudC4KCmBgYHtyIG91dC53aWR0aD0gJzcwJScsIGZpZy5hbGlnbj0nY2VudGVyJ30KZXRzKHVrY2FycywgbW9kZWwgPSAnWlpaJyxkYW1wZWQgPSBUKSAlPiUgc3VtbWFyeSgpCmBgYAoKIyMjQ29tcGFyZSB0aGUgUk1TRSBvZiB0aGUgZml0dGVkIG1vZGVsIHdpdGggdGhlIFJNU0Ugb2YgdGhlIG1vZGVsIHlvdSBvYnRhaW5lZCB1c2luZyBhbiBTVEwgZGVjb21wb3NpdGlvbiB3aXRoIEhvbHQncyBtZXRob2QuIFdoaWNoIGdpdmVzIHRoZSBiZXR0ZXIgaW4tc2FtcGxlIGZpdHM/CgoqIFJNU0Ugb2YgRVRTIEEtQWQtQSBtb2RlbCA9IDI1LjE4NDA5CiogUk1TRSBvZiBIb2x0IEFkZGl2ZSBEYW1wZWQgbW9kZWxvIG9uIFNUTCBkZWNvbXBvc2VkIGRhdGEgPSAyNS4xNTk4NgoKVGhlIEVUUyBnaXZlcyAqbWFyZ2luYWxseSogd29yc2UgcmVzdWx0cy4KCiMjI0NvbXBhcmUgdGhlIGZvcmVjYXN0cyBmcm9tIHRoZSB0d28gYXBwcm9hY2hlcz8gV2hpY2ggc2VlbXMgbW9zdCByZWFzb25hYmxlPwoKR2l2ZW4gaG93IGNsb3NlIHRoZSBSTVNFIHZhbHVlcyBhcmUgd2UgZXhwZWN0IHZlcnkgc2ltaWxhciBmb3JlY2FzdHMuIEFuZCB3ZSBjYW4gc2VlIHRoaXMgaW4gdGhlIHBsb3RzLiBUaGV5IGFyZSB2aXJ0dWFsbHkgaW5kaXN0aW5ndWlzaGFibGUuCgpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CmhvbHRmaXQkbWVhbiAgPC0gaG9sdGZpdCRtZWFuICsgc2Vhc29uYWxhZGp1c3RtZW50c1syOjldCmhvbHRmaXQkdXBwZXIgPC0gaG9sdGZpdCR1cHBlciArIHNlYXNvbmFsYWRqdXN0bWVudHNbMjo5XQpob2x0Zml0JGxvd2VyIDwtIGhvbHRmaXQkbG93ZXIgKyBzZWFzb25hbGFkanVzdG1lbnRzWzI6OV0KaG9sdGZpdCR4ICA8LSBob2x0Zml0JHggKyBzZWFzb25hbGFkanVzdG1lbnRzCmV0cyh1a2NhcnMsIG1vZGVsID0gJ1paWicsZGFtcGVkID0gVCkgJT4lIGZvcmVjYXN0KCkgJT4lIGF1dG9wbG90KCkKaG9sdGZpdCAlPiUgYXV0b3Bsb3QoKQpgYGAKCiMjIEZvciB0aGlzIGV4ZXJjaXNlLCB1c2UgdGhlIG1vbnRobHkgQXVzdHJhbGlhbiBzaG9ydC10ZXJtIG92ZXJzZWFzIHZpc2l0b3JzIGRhdGEsIE1heSAxOTg1LS1BcHJpbCAyMDA1LiAoRGF0YSBzZXQ6IHZpc2l0b3JzLikKCiMjI01ha2UgYSB0aW1lIHBsb3Qgb2YgeW91ciBkYXRhIGFuZCBkZXNjcmliZSB0aGUgbWFpbiBmZWF0dXJlcyBvZiB0aGUgc2VyaWVzLgoKKiBJbmNyZWFzaW5nIHRyZW5kLCBhbG1vc3QgbGluZWFybHkgaW5jcmVhc2luZwoqIENsZWFyIHNlYXNvbmFsaXR5ICh5ZWFybHkpCiMgSW5jcmVhc2luZyB2YXJpYW5jZSBvZiB0aGUgc2Vhc29uYWxpdHkgb3ZlciB0aW1lCiogU2hhcnAgZHJvcCBtaWQtMjAwNCAtIHNvbWV0aGluZyBvZGQgaGFwcGVuZWQgaGVyZQoKYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQphdXRvcGxvdCh2aXNpdG9ycykKYGBgCgojIyNGb3JlY2FzdCB0aGUgbmV4dCB0d28geWVhcnMgdXNpbmcgSG9sdC1XaW50ZXJzJyBtdWx0aXBsaWNhdGl2ZSBtZXRob2QuCmBgYHtyIG91dC53aWR0aD0gJzcwJScsIGZpZy5hbGlnbj0nY2VudGVyJ30KaHcodmlzaXRvcnMsIHNlYXNvbmFsID0gJ20nLCBkYW1wZWQgPSBGKSAlPiUgCiAgICBmb3JlY2FzdChoPTEyKjIpICU+JSBhdXRvcGxvdCgpCmBgYAoKIyMjV2h5IGlzIG11bHRpcGxpY2F0aXZlIHNlYXNvbmFsaXR5IG5lY2Vzc2FyeSBoZXJlPwoKVGhpcyBpcyBiZWNhdXNlIHRoZSBzZWFzb25hbGl0eSBrZWVwcyBpbmNyZWFzaW5nIG92ZXIgdGltZS4gV2UgY2FuIHZpc3VhbGx5IHNlZSB0aGlzIGlmIHdlIGtlZXAgdGhlIHNlYXNvbmFsaXR5IGNvbnN0YW50ICh1c2luZyBgZGVjb21wb3NlYCkuIExvb2sgYXQgdGhlIHJlc2lkdWFscyAtIHRoZSBtYWduaXR1ZGUga2VlcHMgaW5jcmVhc2luZyBvdmVyIHRpbWUuCgpNdWx0aXBsaWNhdGl2ZSBzZWFzb25hbGl0eSBhbGxvd3MgZm9yIGl0IHRvIGluY3JlYXNlIG92ZXIgdGltZS4KCmBgYHtyIG91dC53aWR0aD0gJzcwJScsIGZpZy5hbGlnbj0nY2VudGVyJ30KZGVjb21wb3NlKHZpc2l0b3JzKSAlPiUgcGxvdApgYGAKCiMjI0V4cGVyaW1lbnQgd2l0aCBtYWtpbmcgdGhlIHRyZW5kIGV4cG9uZW50aWFsIGFuZC9vciBkYW1wZWQuCgpBcyBleHBlY3RlZCwgdGhlIGV4cG9uZW50aWFsIG1ldGhvZCBvdmVycHJlZGljdHMgdGhlIHBvaW50IGVzdGltYXRlcyB0aGFuIHRoZSBkYW1wZWQgZm9yZWNhc3QuIEJ1dCwgdGhlIGV4cG9uZW50aWFsIG1ldGhvZCBzZWVtcyB0byBoYXZlIGEgc21hbGxlciBwcmVkaWN0aW9uIGludGVydmFsLgoKYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQpodyh2aXNpdG9ycywgc2Vhc29uYWwgPSAnbScsIGRhbXBlZCA9IFQpICU+JSAKICAgIGZvcmVjYXN0KGg9MTIqMikgJT4lIGF1dG9wbG90KCkKaHcodmlzaXRvcnMsIHNlYXNvbmFsID0gJ20nLCBleHBvbmVudGlhbCA9IFQpICU+JSAKICAgIGZvcmVjYXN0KGg9MTIqMikgJT4lIGF1dG9wbG90KCkKYGBgCgojIyNDb21wYXJlIHRoZSBSTVNFIG9mIHRoZSBvbmUtc3RlcCBmb3JlY2FzdHMgZnJvbSB0aGUgdmFyaW91cyBtZXRob2RzLiBXaGljaCBkbyB5b3UgcHJlZmVyPwoKVGhlIFJNU0VzIGFyZSBmYWlybHkgc2ltaWxhciBmb3IgYWxsIHRocmVlIG1vZGVscy4gQmFzZWQgb24gUk1TRSBJIHdvdWxkIHBpY2sgdGhlIGRhbXBlZCBIb2x0IFdpbnRlcnMgbW9kZWwuIE1BU0UgZm9yIHRoaXMgbW9kZWwgaXMgdGhlIGxvd2VzdCB0b28uCgpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CmJpbmRfcm93cygKaHcodmlzaXRvcnMsIHNlYXNvbmFsID0gJ20nKSAlPiUgZm9yZWNhc3QoaD0xMioyKSAlPiUgCiAgICBhY2N1cmFjeSgpICU+JSBzd2VlcDo6c3dfdGlkeSgpLApodyh2aXNpdG9ycywgc2Vhc29uYWwgPSAnbScsIGRhbXBlZCA9IFQpICU+JSBmb3JlY2FzdChoPTEyKjIpICU+JSAKICAgIGFjY3VyYWN5KCkgJT4lIHN3ZWVwOjpzd190aWR5KCksCmh3KHZpc2l0b3JzLCBzZWFzb25hbCA9ICdtJywgZXhwb25lbnRpYWwgPSBUKSAlPiUgZm9yZWNhc3QoaD0xMioyKSAlPiUgCiAgICBhY2N1cmFjeSgpICU+JSBzd2VlcDo6c3dfdGlkeSgpCikgJT4lIG11dGF0ZSgucm93bmFtZXMgPSBjKCdod19tJywnaHdfbV9kJywnaHdfbV9lJykpICU+JSAKICAgIHJlbmFtZShtb2RlbCA9IC5yb3duYW1lcykKYGBgCgojIyNOb3cgZml0IGVhY2ggb2YgdGhlIGZvbGxvd2luZyBtb2RlbHMgdG8gdGhlIHNhbWUgZGF0YToKCiMjIyNhIG11bHRpcGxpY2F0aXZlIEhvbHQtV2ludGVycycgbWV0aG9kOwpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9Cmh3X20gPC0gaHcodmlzaXRvcnMsIHNlYXNvbmFsID0gJ20nKSAlPiUgZm9yZWNhc3QoaD0xMioyKQpod19tICU+JSBhdXRvcGxvdCgpCmBgYAoKIyMjI2FuIEVUUyBtb2RlbDsKYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQpldHNfZml0IDwtIGV0cyh2aXNpdG9ycykgJT4lIGZvcmVjYXN0KGg9MTIqMikKZXRzX2ZpdCAlPiUgYXV0b3Bsb3QoKQpgYGAKCiMjIyNhbiBhZGRpdGl2ZSBFVFMgbW9kZWwgYXBwbGllZCB0byBhIEJveC1Db3ggdHJhbnNmb3JtZWQgc2VyaWVzOwpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CmV0c19ib3hfZml0IDwtIGV0cyh2aXNpdG9ycywgbGFtYmRhID0gJ2F1dG8nKSAlPiUgZm9yZWNhc3QoaD0xMioyKQpldHNfYm94X2ZpdCAlPiUgYXV0b3Bsb3QoKQpgYGAKCiMjIyNhIHNlYXNvbmFsIG5haXZlIG1ldGhvZCBhcHBsaWVkIHRvIHRoZSBCb3gtQ294IHRyYW5zZm9ybWVkIHNlcmllczsKYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQpzbmFpdmVfYm94X2ZpdCA8LSBzbmFpdmUodmlzaXRvcnMsIGxhbWJkYSA9ICdhdXRvJykgJT4lIGZvcmVjYXN0KGg9MTIqMikKc25haXZlX2JveF9maXQgJT4lIGF1dG9wbG90KCkKYGBgCgojIyMjYW4gU1RMIGRlY29tcG9zaXRpb24gYXBwbGllZCB0byB0aGUgQm94LUNveCB0cmFuc2Zvcm1lZCBkYXRhIGZvbGxvd2VkIGJ5IGFuIEVUUyBtb2RlbCBhcHBsaWVkIHRvIHRoZSBzZWFzb25hbGx5IGFkanVzdGVkICh0cmFuc2Zvcm1lZCkgZGF0YS4KClRoaXMgY29kZSBwZXJmb3JtcyB0aGUgbmVlZGVkIGFjdGlvbnMuIFRoZSBgc3RsYCBmdW5jdGlvbiByZW1vdmVzIHRoZSB0cmVuZCBmcm9tIHRoZSBzaWduYWwgdmVyeSB3ZWxsLiBUaGUgcmVzaWR1YWxzIGxvb2sgZmFpcmx5IHJhbmRvbSB2aXN1YWxseS4KCkFmdGVyIGFkanVzdGluZyBmb3Igc2Vhc29uYWxpdHksIHdlIGNhbiBzZWUgdGhhdCBhbiBFVFMgbW9kZWwgaXMgYW4gQS1BZC1OIG1vZGVsOiBubyBzZWFzb25hbGl0eSB3aXRoIGEgbGluZWFyIGRhbXBlZCB0cmVuZC4gTm93LCBhbHRob3VnaCB3ZSBoYXZlIGFuIGFkZGl0aXZlIHRyZW5kIGNvbXBvbmVudCwgdGhlIGJldGEgY29lZWYgaXMgMWUtNCwgc28gcHJhY3RpY2FsbHksIHRoZSBmb3JlY2FzdCBpcyBmbGF0IGFzIHdlIGNhbiBzZWUgaW4gdGhlIHBsb3QuCgpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CkJveENveC5sYW1iZGEoeCA9IHZpc2l0b3JzKQp2aXNpdG9yc19ib3hlZCA8LSBCb3hDb3goeCA9IHZpc2l0b3JzLCBsYW1iZGEgPSAwLjI3NzUyNDkpCnZpc2l0b3JzX3N0bCA8LSBzdGwodmlzaXRvcnNfYm94ZWQsIHMud2luZG93ID0gJ3BlcmlvZGljJykKcGxvdCh2aXNpdG9yc19zdGwpCnZpc2l0b3JzX3NhZGogPC0gc2Vhc2Fkaih2aXNpdG9yc19zdGwpCmV0c19zYWRqIDwtIGV0cyh2aXNpdG9yc19zYWRqLCBtb2RlbCA9ICdaWlonLCBkYW1wZWQgPSBUKQpldHNfc2FkaiAlPiUgc3VtbWFyeSgpCmV0c19zYWRqICU+JSBmb3JlY2FzdCgpICU+JSBhdXRvcGxvdCgpCmBgYAoKIyMjRm9yIGVhY2ggbW9kZWwsIGxvb2sgYXQgdGhlIHJlc2lkdWFsIGRpYWdub3N0aWNzIGFuZCBjb21wYXJlIHRoZSBmb3JlY2FzdHMgZm9yIHRoZSBuZXh0IHR3byB5ZWFycy4gV2hpY2ggZG8geW91IHByZWZlcj8KClRoZSByZXNpZHVhbHMgdGVsbCB1cyB0aGlzOgoKKiBIVyAtIFRoZXJlIGlzIHNpZ25pZmljYW50IGF1dG9jb3JyZWxhdGlvbiBpbiB0aGUgbW9kZWwgKDEyLDI0LDM2IHBlcmlvZHMpLiBQLXZhbHVlIGlzIGxvdyB0b28uCiogRVRTIC0gQWxtb3N0IG5vIGF1dG9jb3JyZWxhdGlvbi4gSnVzdCBhIGJpdCBhdCAxMiBwZXJpb2RzLiBQLXZhbHVlIGlzIGxvdywgYnV0IHdlIGRvIGhhdmUgYSBsYXJnZSBzYW1wbGUgc2l6ZSwgc28gdGhlIHRlc3Qgd2lsbCBiZSBzZW5zaXRpdmUuCiogRVRTLUJveENveGVkIC0gQWxtb3N0IG5vIGF1dG9jb3JyZWxhdGlvbi4KKiBTTmFpdmUgLSBMT1RTIG9mIGF1dG9jb3JyZWxhdGlvbiAoYXMgZXhwZWN0ZWQpLgoqIEVUUyBTQWRqdXN0ZWQgLSBObyBhdXRvY29ycmVsYXRpb24uCgpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CmNoZWNrcmVzaWR1YWxzKGh3X20pCmNoZWNrcmVzaWR1YWxzKGV0c19maXQpCmNoZWNrcmVzaWR1YWxzKGV0c19ib3hfZml0KQpjaGVja3Jlc2lkdWFscyhzbmFpdmVfYm94X2ZpdCkKY2hlY2tyZXNpZHVhbHMoZXRzX3NhZGopCmBgYCAKClB1dHRpbmcgZXZlcnl0aGluZyB0b2dldGhlciwgd2UgY2FuIHNlZSB0aGF0OgoKKiBTZWFzb25hbCBuYWl2ZSB1bmRlcnByZWRpY3RzIHNpbmNlIGl0IGNhbid0IGFjY291bnQgZm9yIHRoZSBpbmNyZWFzaW5nIHRyZW5kIGNvbXBvbmVudAoqIFRoZSBCb3hDb3grU1RMK0VUUyBtZXRob2QgYWxzbyB1bmRlcnByZWRpY3RzCiogSFcgc2VlbXMgcmVhc29uYWJsZQoqIEVUUyBzZWVtcyByZWFzb25hYmxlIGFzIHdlbGwgCiogRVRTLUJveENveGVkIHNlZW1zIHRvIG92ZXIgcHJlZGljdAoKR2l2ZW4gdGhpcyBpbmZvcm1hdGlvbiBhbmQgdGhlIHJlc2lkdWFscywgSSB3b3VsZCBwcm9iYWJseSBwaWNrIHRoZSBFVFMgbW9kZWwuIEl0IGhhcyBhbG1vc3Qgbm8gYXV0b2NvcnJlbGF0aW9uIGFuZCBzZWVtcyB0byBmaXQgdGhlIGRhdGEgdGhlIGJlc3QuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpod19tID0gaHdfbSAlPiUgZm9yZWNhc3QoaCA9IDEyKjIpICU+JSBzd2VlcDo6c3dfdGlkeSgpICU+JSAKICAgIHB1bGwoJ1BvaW50IEZvcmVjYXN0JykgJT4lIHRzKGZyZXF1ZW5jeSA9IDEyLCBzdGFydD1jKDIwMDUsNSkpCmV0c19maXQgPSBldHNfZml0ICU+JSBmb3JlY2FzdChoID0gMTIqMikgJT4lIHN3ZWVwOjpzd190aWR5KCkgJT4lIAogICAgcHVsbCgnUG9pbnQgRm9yZWNhc3QnKSAlPiUgdHMoZnJlcXVlbmN5ID0gMTIsIHN0YXJ0PWMoMjAwNSw1KSkKZXRzX2JveF9maXQgPSBldHNfYm94X2ZpdCAlPiUgZm9yZWNhc3QoaCA9IDEyKjIpICU+JSBzd2VlcDo6c3dfdGlkeSgpICU+JSAKICAgIHB1bGwoJ1BvaW50IEZvcmVjYXN0JykgJT4lIHRzKGZyZXF1ZW5jeSA9IDEyLCBzdGFydD1jKDIwMDUsNSkpCnNuYWl2ZV9ib3hfZml0ID0gc25haXZlX2JveF9maXQgJT4lIGZvcmVjYXN0KGggPSAxMioyKSAlPiUgc3dlZXA6OnN3X3RpZHkoKSAlPiUgCiAgICBwdWxsKCdQb2ludCBGb3JlY2FzdCcpICU+JSB0cyhmcmVxdWVuY3kgPSAxMiwgc3RhcnQ9YygyMDA1LDUpKQpldHNfcmVzZWFzb25lZCA9IGV0c19zYWRqICU+JSBmb3JlY2FzdChoID0gMTIqMikgJT4lIHN3ZWVwOjpzd190aWR5KCkgJT4lIAogICAgcHVsbCgnUG9pbnQgRm9yZWNhc3QnKSAlPiUgdHMoZnJlcXVlbmN5ID0gMTIsIHN0YXJ0PWMoMjAwNSw1KSkKZXRzX3Jlc2Vhc29uZWQgPSAoZXRzX3Jlc2Vhc29uZWQgKyB2aXNpdG9yc19zdGwkdGltZS5zZXJpZXMgJT4lIAogICAgICAgICAgICAgICAgICAgICAgdGFpbCgyNCkgJT4lIC5bLCdzZWFzb25hbCddICU+JSAKICAgICAgICAgICAgICAgICAgICAgIGFzLm51bWVyaWMoKSkgJT4lIEludkJveENveChsYW1iZGEgPSAwLjI3NzUyNDkpCgp0cy51bmlvbih2aXNpdG9ycywgaHdfbSwgZXRzX2ZpdCwgZXRzX2JveF9maXQsIHNuYWl2ZV9ib3hfZml0LCBldHNfcmVzZWFzb25lZCkgJT4lIAogICAgYXV0b3Bsb3Qoc2l6ZT0yKSArIHhsaW0oYygyMDAwLDIwMDgpKQpgYGAKCiMgU2VjdGlvbiA4LjExCgojI1VzZSBSIHRvIHNpbXVsYXRlIGFuZCBwbG90IHNvbWUgZGF0YSBmcm9tIHNpbXBsZSBBUklNQSBtb2RlbHMuCiMjI1VzZSB0aGUgZm9sbG93aW5nIFIgY29kZSB0byBnZW5lcmF0ZSBkYXRhIGZyb20gYW4gQVIoMSkgbW9kZWwgd2l0aCBwaGlfMT0wLjYgYW5kIHNpZ21hXzI9MS4gVGhlIHByb2Nlc3Mgc3RhcnRzIHdpdGggeTA9MC4KYGBge3J9CnkgPC0gdHMoZGF0YSA9IG51bWVyaWMoMTAwKSkKZSA8LSBybm9ybShuID0gMTAwLCBtZWFuID0gMCwgc2QgPSAxKQpmb3IoaSBpbiAyOjEwMCkKICB5W2ldIDwtIDAuNip5W2ktMV0gKyBlW2ldCmBgYAoKIyMjUHJvZHVjZSBhIHRpbWUgcGxvdCBmb3IgdGhlIHNlcmllcy4gSG93IGRvZXMgdGhlIHBsb3QgY2hhbmdlIGFzIHlvdSBjaGFuZ2UgcGhpXzE/CmBgYHtyIG91dC53aWR0aD0gJzcwJScsIGZpZy5hbGlnbj0nY2VudGVyJ30KcGxvdCh5KQpgYGAKYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQpwaGkxIDwtIHNlcSgtMSwgMSwgYnkgPSAwLjUpCnBoaTEKeSA8LSB0cyhkYXRhID0gbnVtZXJpYygxMDApKQplIDwtIHJub3JtKG4gPSAxMDAsIG1lYW4gPSAwLCBzZCA9IDEpCnJlc3VsdCA8LSBtYXRyaXgobnJvdyA9IDEwMCxuY29sID0gbGVuZ3RoKHBoaTEpKQpmb3IocCBpbiBzZXFfYWxvbmcocGhpMSkpewogICAgZm9yKGkgaW4gMjoxMDApCiAgICAgIHlbaV0gPC0gcGhpMVtwXSp5W2ktMV0gKyBlW2ldCiAgICByZXN1bHRbLHBdIDwtIHkKfQpwbG90IChyZXN1bHRbLDFdLCB0eXBlPSdsJywgY29sPSdncmF5JykKbGluZXMocmVzdWx0WywyXSwgdHlwZT0nbCcsIGNvbD0ncmVkJykKbGluZXMocmVzdWx0WywzXSwgdHlwZT0nbCcsIGNvbD0nYmx1ZScpCmxpbmVzKHJlc3VsdFssNF0sIHR5cGU9J2wnLCBjb2w9J2dyZWVuJykKbGluZXMocmVzdWx0Wyw1XSwgdHlwZT0nbCcsIGNvbD0nb3JhbmdlJykKYGBgCgpPdmVyIHRoZSByYW5nZSBvZiB2YWx1ZXMgb2YgcGhpXzEgZnJvbSAtMSB0byAxOgoKKiB3aGVuIGMgPSAwLCBwaGkgPCAwIC0tIHdlIGNhbiBzZWUgdGhhdCB0aGUgdHJlbmQgb2NjaWxhdGVzIGJldHdlZW4gK3ZlIGFuZCAtdmUuIChncmF5IGFuZCByZWQgbGluZXMpCiogd2hlbiBjID0gMCwgcGhpID0gMCAtLSB3aGl0ZSBub2lzZQoqIHdoZW4gYyA9IDAsIHBoaSA+IDAgLS0gcmFuZG9tIHdhbGsgKG5vIGRyaWZ0KSAoZ3JlZW4gYW5kIG9yYW5nZSBsaW5lcykKCiMjI1dyaXRlIHlvdXIgb3duIGNvZGUgdG8gZ2VuZXJhdGUgZGF0YSBmcm9tIGFuIE1BKDEpIG1vZGVsIHdpdGggdGhldGFfMT0wLjYgYW5kIHNpZ21hXzI9MS4KYGBge3J9CnkgPC0gdHMoZGF0YSA9IG51bWVyaWMoMTAwKSkKZSA8LSBybm9ybShuID0gMTAwLCBtZWFuID0gMCwgc2QgPSAxKQpmb3IgKGkgaW4gMjoxMDApewogICAgeVtpXSA8LSAwLjYqZVtpLTFdICsgZVtpXQp9CmBgYAojIyNQcm9kdWNlIGEgdGltZSBwbG90IGZvciB0aGUgc2VyaWVzLiBIb3cgZG9lcyB0aGUgcGxvdCBjaGFuZ2UgYXMgeW91IGNoYW5nZSB0aGV0YV8xPwpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CnBsb3QoeSkKYGBgCmBgYHtyIG91dC53aWR0aD0gJzcwJScsIGZpZy5hbGlnbj0nY2VudGVyJ30KdGhldGExIDwtIHNlcSgtMSwgMSwgYnkgPSAwLjUpCnRoZXRhMQp5IDwtIHRzKGRhdGEgPSBudW1lcmljKDEwMCkpCmUgPC0gcm5vcm0obiA9IDEwMCwgbWVhbiA9IDAsIHNkID0gMSkKcmVzdWx0IDwtIG1hdHJpeChucm93ID0gMTAwLG5jb2wgPSBsZW5ndGgodGhldGExKSkKZm9yKHRodCBpbiBzZXFfYWxvbmcodGhldGExKSl7CiAgICBmb3IoaSBpbiAyOjEwMCkKICAgICAgeVtpXSA8LSB0aGV0YTFbdGh0XSplW2ktMV0gKyBlW2ldCiAgICByZXN1bHRbLHRodF0gPC0geQp9CnBsb3QgKHJlc3VsdFssMV0sIHR5cGU9J2wnLCBjb2w9J2dyYXknKQpsaW5lcyhyZXN1bHRbLDJdLCB0eXBlPSdsJywgY29sPSdyZWQnKQpsaW5lcyhyZXN1bHRbLDNdLCB0eXBlPSdsJywgY29sPSdibHVlJykKbGluZXMocmVzdWx0Wyw0XSwgdHlwZT0nbCcsIGNvbD0nZ3JlZW4nKQpsaW5lcyhyZXN1bHRbLDVdLCB0eXBlPSdsJywgY29sPSdvcmFuZ2UnKQpgYGAKClRoZXJlIGRvZXNuJ3Qgc2VlbSB0byBhbnkgY29uY3JldGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhldGEgYW5kIHRoZSByZXN1bHQuCgojIyNHZW5lcmF0ZSBkYXRhIGZyb20gYW4gQVJNQSgxLDEpIG1vZGVsIHdpdGggcGhpXzEgPSAwLjYgYW5kIHRoZXRhXzE9MC42IGFuZCBzaWdtYV8yPTEuCmBgYHtyIG91dC53aWR0aD0gJzcwJScsIGZpZy5hbGlnbj0nY2VudGVyJ30KeSA8LSB0cyhudW1lcmljKDEwMCkpCmUgPC0gcm5vcm0oMTAwKQpmb3IoaSBpbiAyOjEwMCkKICAgIHlbaV0gPC0gMC42ICogeVtpIC0gMV0gKyAwLjYgKiBlW2kgLSAxXSArIGVbaV0KcGxvdCh5KQpgYGAKCiMjI0dlbmVyYXRlIGRhdGEgZnJvbSBhbiBBUigyKSBtb2RlbCB3aXRoIHBoaV8xPS0wLjggYW5kIHBoaV8yPTAuMyBhbmQgc2lnbWFfMj0xLiAoTm90ZSB0aGF0IHRoZXNlIHBhcmFtZXRlcnMgd2lsbCBnaXZlIGEgbm9uLXN0YXRpb25hcnkgc2VyaWVzLikKYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQp5IDwtIHRzKG51bWVyaWMoMTAwKSkKZSA8LSBybm9ybSgxMDApCmZvcihpIGluIDM6MTAwKQogICAgeVtpXSA8LSAwLjggKiB5W2kgLSAxXSArIDAuMyAqIHlbaSAtIDJdICsgZVtpXQpwbG90KHkpCmBgYAoKIyMjR3JhcGggdGhlIGxhdHRlciB0d28gc2VyaWVzIGFuZCBjb21wYXJlIHRoZW0uCgpHcmFwaGVkIGFib3ZlLiBXaGlsZSB0aGUgQVJNQSgxLDEpIGdpdmVzIGEgc3RhdGlvbmFyeSBzZXJpZXMsIHRoZSBBUigyKSBpcyBjbGVhcmx5IG5vbi1zdGF0aW9uYXJ5LgoKIyNDb25zaWRlciB0aGUgbnVtYmVyIG9mIHdvbWVuIG11cmRlcmVkIGVhY2ggeWVhciAocGVyIDEwMCwwMDAgc3RhbmRhcmQgcG9wdWxhdGlvbikgaW4gdGhlIFVuaXRlZCBTdGF0ZXMgKGRhdGEgc2V0IHdtdXJkZXJzKS4KIyMjQnkgc3R1ZHlpbmcgYXBwcm9wcmlhdGUgZ3JhcGhzIG9mIHRoZSBzZXJpZXMgaW4gUiwgZmluZCBhbiBhcHByb3ByaWF0ZSBBUklNQShwLGQscSkgbW9kZWwgZm9yIHRoZXNlIGRhdGEuCmBgYHtyIG91dC53aWR0aD0gJzcwJScsIGZpZy5hbGlnbj0nY2VudGVyJ30KZ2d0c2Rpc3BsYXkod211cmRlcnMpCmBgYAoqIFRoZSB0aW1lIHNlcmllcyBoYXMgdHJlbmRpbmcgYmVoYXZpb3VyLiBJdCBkb2Vzbid0IHNlZW0gdG8gaGF2ZSBhbnkgc2Vhc29uYWwgYmVoYXZpb3IuCgpgYGB7cn0Ka3Bzcy50ZXN0KHdtdXJkZXJzKQpgYGAKVGhlIEtQU1MgdGVzdCBzaG93cyB1cyB0aGF0IHdlIG11c3QgcmVqZWN0IHRoZSBIMDogZGF0YSBhcmUgc3RhdGlvbmFyeS4gRGlmZmVyZW5jaW5nIHRoZSB0aW1lIHNlcmllcyBieSAxOgpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CmdndHNkaXNwbGF5KGRpZmYod211cmRlcnMsbGFnID0gMSkpCmBgYAoKKiBBZnRlciBkaWZmZXJlbmNpbmcsIHRoZSB0aW1lIHNlcmllcyBzZWVtcyB0byBiZSBmYWlybHkgc3RhdGlvbmFyeQoqIEFDRiBzaG93cyB1cyBhIHNpZ25pZmljYW50IDJuZCBvcmRlciBjb21wb25lbnQuIFRoZSBQQUNGIGFsc28gc2hvd3MgYSAybmQgb3JkZXIgc2lnbmlmaWNhbnQgY29tcG9uZW50LgoKYGBge3J9Cmtwc3MudGVzdChkaWZmKHdtdXJkZXJzLDEpKQpgYGAKVHJ5aW5nIG9uZSBtb3JlIGRpZmZlcmVuY2luZyBzY2hlbWUuIERvdWJsZSBkaWZmZXJlbmNpbmcgLSBEaWZmZXJlbmNpbmcgdGhlIGRpZmZlcmVuY2UgYnkgMToKYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQpnZ3RzZGlzcGxheShkaWZmKGRpZmYod211cmRlcnMsbGFnID0gMSkpKQpgYGAKCiogUEFDRiByZWR1Y2VzIGV4cG9uZW50aWFsbHkuCiogQUNGIGhhcyBhIHNpZ25pZmljYW50IG9yZGVyIDIsIGFuZCBubyBzaWduaWZpY2FudCBvcmRlcnMgYWZ0ZXIgdGhhdC4KClRodXMsIEkgY2FuIGd1ZXNzIHRoYXQgYW4gQVJJTUEoMCwgMiwgMikgbWlnaHQgZml0IHRoZSBkYXRhIHdlbGwuCgojIyNTaG91bGQgeW91IGluY2x1ZGUgYSBjb25zdGFudCBpbiB0aGUgbW9kZWw/IEV4cGxhaW4uClllcywgSSB0aGluayB3ZSBzaG91bGQgaW5jbHVkZSBhIGNvbnN0YW50LiBXaXRoIGEgY29uc3RhbnQgdmFsdWUgaW5jbHVkZWQsIHdpdGggYSBkPTEgb3IgZD0yLCBhIGxvbmcgdGVybSBmb3JlY2FzdCBjYW4gZm9sbG93IGEgbGluZSBvciBxdWFkcmF0aWMgY3VydmUuIFRoZSB0aW1lIHNlcmllcyBzZWVtcyB0byBiZSBkb3dud2FyZCB0cmVuZGluZywgc28gYSBjb25zdGFudCB3aWxsIGhlbHAuIElmIGMgPSAwLCB3aXRoIGQgPSAwIG9yIGQgPSAxLCB3ZSB3b24ndCBnZXQgYSBkb3dud2FyZCB0cmVuZC4gKFdpdGggYyA9IDAgYW5kIGQgPSAyLCB3ZSBjYW4gc3RpbGwgZ2V0IGEgbGluZWFyIHRyZW5kKS4KCiMjI1dyaXRlIHRoaXMgbW9kZWwgaW4gdGVybXMgb2YgdGhlIGJhY2tzaGlmdCBvcGVyYXRvci4KCiAgICAoMSAtIEIpXjIgeV90ID0gYyArICgxICsgdGhldGExIHggQiArIHRoZXRhMiB4IEJeMikgeCBlX3QKCiMjI0ZpdCB0aGUgbW9kZWwgdXNpbmcgUiBhbmQgZXhhbWluZSB0aGUgcmVzaWR1YWxzLiBJcyB0aGUgbW9kZWwgc2F0aXNmYWN0b3J5PwpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CmZpdDEgPC0gQXJpbWEod211cmRlcnMsIG9yZGVyID0gYygwLCAyLCAyKSkKc3VtbWFyeShmaXQxKQpjaGVja3Jlc2lkdWFscyhmaXQxKQpgYGAKVGhlIExqdW5nLUJveCB0ZXN0IHNob3dzIGEgcC12YWwgPiAwLjA1LCBzbyB0aGVyZSBpcyBsaXR0bGUgY2hhbmNlIG9mIGF1dG9jb3JyZWxhdGlvbiBpbiB0aGUgcmVzaWR1YWxzLiBWaXN1YWxseSBsb29raW5nIGF0IHRoZSBBQ0YgcGxvdCwgd2UgY2FuIHNlZSBzb21lIHRoZXJlIGlzIHBlcmlvZGljIGVsZW1lbnRzIGluIHRoZSByZXNpZHVhbHMuIFRoZSBtb2RlbCBzZWVtcyBhZGVxdWF0ZS4KCiMjI0ZvcmVjYXN0IHRocmVlIHRpbWVzIGFoZWFkLiBDaGVjayB5b3VyIGZvcmVjYXN0cyBieSBoYW5kIHRvIG1ha2Ugc3VyZSB5b3Uga25vdyBob3cgdGhleSBoYXZlIGJlZW4gY2FsY3VsYXRlZC4KYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQpmaXQxICU+JSBmb3JlY2FzdChoID0gMykKYGBgCgojIyNDcmVhdGUgYSBwbG90IG9mIHRoZSBzZXJpZXMgd2l0aCBmb3JlY2FzdHMgYW5kIHByZWRpY3Rpb24gaW50ZXJ2YWxzIGZvciB0aGUgbmV4dCB0aHJlZSBwZXJpb2RzIHNob3duLgpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CmZpdDEgJT4lIGZvcmVjYXN0KGggPSAzKSAlPiUgYXV0b3Bsb3QoKQpgYGAKCiMjI0RvZXMgYXV0by5hcmltYSBnaXZlIHRoZSBzYW1lIG1vZGVsIHlvdSBoYXZlIGNob3Nlbj8gSWYgbm90LCB3aGljaCBtb2RlbCBkbyB5b3UgdGhpbmsgaXMgYmV0dGVyPwpJZiB3ZSBhbGxvdyBgc3RlcHdpc2VgOgpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CmF1dG8uYXJpbWEod211cmRlcnMsIHNlYXNvbmFsID0gRiwgYWxsb3dkcmlmdCA9IFQpCmBgYApJZiB3ZSBmb3JjZSBhIHRob3JvdWdoIGludmVzdGlnYXRpb246CmBgYHtyIG91dC53aWR0aD0gJzcwJScsIGZpZy5hbGlnbj0nY2VudGVyJ30KYXV0by5hcmltYSh3bXVyZGVycywgc2Vhc29uYWwgPSBULCBzdGVwd2lzZSA9IEYsIGFsbG93ZHJpZnQgPSBUKQpgYGAKClRoZSBBUklNQSgwLDIsMykgaGFzIHRoZSBsb3dlc3QgQUlDYyB2YWx1ZSAoLTYuNykgYWdhaW5zdCBteSBzZWxlY3Rpb24gb2YgQVJJTUEoMCwyLDIpIHdpdGggYW4gQUlDYyBvZiAtNS43LgoKIyNDb25zaWRlciB0aGUgcXVhcnRlcmx5IG51bWJlciBvZiBpbnRlcm5hdGlvbmFsIHRvdXJpc3RzIHRvIEF1c3RyYWxpYSBmb3IgdGhlIHBlcmlvZCAxOTk54oCTMjAxMC4gKERhdGEgc2V0IGF1c3RvdXJpc3RzLikKIyMjRGVzY3JpYmUgdGhlIHRpbWUgcGxvdC4KYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQphdXRvcGxvdChhdXN0b3VyaXN0cykKYGBgCiogVHJlbmQgY29tcG9uZW50IGV4aXN0cwoqIFNlYXNvbmFsaXR5IGV4aXN0cwoqIFNlYXNvbmFsaXR5IGluY3JlYXNlcyBvdmVyIHRpbWUKCkknbSBnb2luZyB0byBsb2cgdHJhbnNmb3JtIHRoZSBkYXRhCmBgYHtyfQpsYXVzdG91cmlzdHMgPC0gbG9nKGF1c3RvdXJpc3RzKQphdXRvcGxvdChsYXVzdG91cmlzdHMpCmBgYAoKIyMjV2hhdCBjYW4geW91IGxlYXJuIGZyb20gdGhlIEFDRiBncmFwaD8KYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQpnZ0FjZihsYXVzdG91cmlzdHMpCmBgYApWZXJ5IHN0cm9uZyBzZWFzb25hbGl0eSAtIDQsIDgsIDEyLCAxNiBzZWVuIGluIHRoZSBjb21wb25lbnRzLiBSZWR1Y2luZyBzdHJlbmd0aCBvZiB0aGUgY29udHJpYnV0aW9ucyBhcyBleHBlY3RlZC4KCiMjI1doYXQgY2FuIHlvdSBsZWFybiBmcm9tIHRoZSBQQUNGIGdyYXBoPwpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CmdnUGFjZihsYXVzdG91cmlzdHMpCmBgYApTdHJvbmcgc2Vhc29uYWwgY29tcG9uZW50cyBhdCBsYWc9NC4gUGVyaGFwcyBhIHN0cm9uZyBub24tc2Vhc29uYWwgY29tcG9uZW50IGF0IGxhZz01LgoKIyMjUHJvZHVjZSBwbG90cyBvZiB0aGUgc2Vhc29uYWxseSBkaWZmZXJlbmNlZCBkYXRhICgxLUJeNClZdC4gV2hhdCBtb2RlbCBkbyB0aGVzZSBncmFwaHMgc3VnZ2VzdD8KCkFmdGVyIGFkanVzdGluZyBmb3Igc2Vhc29uYWxpdHksIGFuZCBkaWZmZXJlbmNpbmcgdGhlIGRhdGEgb25jZSB0byBtYWtlIGl0IHN0YXRpb25hcnksIHRoZSBBQ0YgYW5kIFBBQ0YgcGxvdHMgdGVsbCBtZSBhIHNpZ25pZmljYW50IGxhZyBvZiAxIGV4aXN0cy4gSXQncyBoYXJkIGZvciBtZSB0byBwaWNrIG9mIHA9MSBvciBxPTEgYmFzZWQgb24gdGhpcyBwbG90IGFsb25lLgoKYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQp0YXVzdG91X2FkaiA8LSBzZWFzYWRqKHN0bChsYXVzdG91cmlzdHMsIHMud2luZG93ID0gJ3BlcmlvZGljJykpCmF1dG9wbG90KHRhdXN0b3VfYWRqKQpnZ3RzZGlzcGxheShkaWZmKHRhdXN0b3VfYWRqLDEpKQpgYGAKClBlcmhhcHMgSSBtaWdodCBjaG9vc2UgYSBBUklNQSgxLDAsMCkoMCwxLDEpWzRdIG9yIGEgQVJJTUEoMCwwLDEpKDAsMSwxKVs0XS4gQnV0IEkgY2FuJ3QgcGljayB3aXRob3V0IG1vcmUgYW5hbHlzaXMuCgojIyNEb2VzIGF1dG8uYXJpbWEgZ2l2ZSB0aGUgc2FtZSBtb2RlbCB0aGF0IHlvdSBjaG9zZT8gSWYgbm90LCB3aGljaCBtb2RlbCBkbyB5b3UgdGhpbmsgaXMgYmV0dGVyPwpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CmF1dG8uYXJpbWEobGF1c3RvdXJpc3RzLCBzdGVwd2lzZSA9IEYpCmBgYApJdCdzIHNpbWlsYXIgdG8gd2hhdCBJIHBpY2tlZCwgYW5kIEkgdGVuZCB0byBhZ3JlZSB3aXRoIGl0J3Mgc2VsZWN0aW9uLgoKIyMjV3JpdGUgdGhlIG1vZGVsIGluIHRlcm1zIG9mIHRoZSBiYWNrc2hpZnQgb3BlcmF0b3IsIGFuZCB0aGVuIHdpdGhvdXQgdXNpbmcgdGhlIGJhY2tzaGlmdCBvcGVyYXRvci4KCiAgICAoMSAtIHBoaTEgeCBCKSgxIC0gUEhJMSB4IEJeNCkoMSAtIEJeNCl5X3QgPSAoMSArIFRIRVRBMSB4IEJeNCllX3QKCiMjQ29uc2lkZXIgdGhlIHRvdGFsIG5ldCBnZW5lcmF0aW9uIG9mIGVsZWN0cmljaXR5IChpbiBiaWxsaW9uIGtpbG93YXR0IGhvdXJzKSBieSB0aGUgVS5TLiBlbGVjdHJpYyBpbmR1c3RyeSAobW9udGhseSBmb3IgdGhlIHBlcmlvZCAxOTg14oCTMTk5NikuIChEYXRhIHNldCB1c21lbGVjLikgSW4gZ2VuZXJhbCB0aGVyZSBhcmUgdHdvIHBlYWtzIHBlciB5ZWFyOiBpbiBtaWQtc3VtbWVyIGFuZCBtaWQtd2ludGVyLgojIyNFeGFtaW5lIHRoZSAxMi1tb250aCBtb3ZpbmcgYXZlcmFnZSBvZiB0aGlzIHNlcmllcyB0byBzZWUgd2hhdCBraW5kIG9mIHRyZW5kIGlzIGludm9sdmVkLgpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CmF1dG9wbG90KHVzbWVsZWMpCm1hKHVzbWVsZWMsIG9yZGVyID0gMTIpICU+JSBhdXRvcGxvdCgpCmBgYAoKIyMjRG8gdGhlIGRhdGEgbmVlZCB0cmFuc2Zvcm1pbmc/IElmIHNvLCBmaW5kIGEgc3VpdGFibGUgdHJhbnNmb3JtYXRpb24uCgpZZXMsIHRoZXJlIGlzIGFuIGluY3JlYXNpbmcgc2Vhc29uYWxpdHkuIExvb2tzIGxpa2UgYW4gaW52ZXJzZSBzcXJ0IHdpbGwgd29yay4KYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQpCb3hDb3gubGFtYmRhKHVzbWVsZWMpCmBgYApgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9Cmx1c21lbGVjIDwtIEJveENveCh1c21lbGVjLCBsYW1iZGEgPSAtMC40NzcyNDAyKQphdXRvcGxvdChsdXNtZWxlYykKYGBgCgojIyNBcmUgdGhlIGRhdGEgc3RhdGlvbmFyeT8gSWYgbm90LCBmaW5kIGFuIGFwcHJvcHJpYXRlIGRpZmZlcmVuY2luZyB3aGljaCB5aWVsZHMgc3RhdGlvbmFyeSBkYXRhLgoKVGhlIGRhdGEgYXJlIG5vdCBzdGF0aW9uYXJ5LiBBIGRpZmZlcmVuY2Ugb2YgMSBpcyBuZWVkZWQgdG8gbWFrZSBpdCBzdGF0aW9uYXJ5IGFjY29yZGluZyB0byB0aGUgS1BTUyB0ZXN0LgpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CmRpZmYobHVzbWVsZWMsMSkgJT4lICBrcHNzLnRlc3QoKQpgYGAKVmlzdWFsbHksIHRoZSBzaWduYWwgbG9va3Mgc3RhdGlvbmFyeS4KYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQpsdXNtZWxlY19kaWZmIDwtIGRpZmYobHVzbWVsZWMpCmx1c21lbGVjX2RpZmYgJT4lIGF1dG9wbG90KCkKYGBgClpvb21pbmcgaW4gYSBiaXQgbW9yZSwgSSB0aGluayBJIGNhbiBzZWUgc2Vhc29uYWxpdHkgd2hpY2ggYSBkaWZmPTEgaGFzbid0IGdvdCByaWQgb2YuCmBgYHtyIG91dC53aWR0aD0gJzcwJScsIGZpZy5hbGlnbj0nY2VudGVyJ30KbHVzbWVsZWNfZGlmZiAlPiUgd2luZG93KHN0YXJ0PTIwMDApICU+JSBhdXRvcGxvdCgpCmBgYAoKSWYgd2UgbG9vayBhdCB0aGUgQUNGLCBQQUNGIHBsb3RzLCB3ZSBjYW4gc2VlIHR3byBwYXR0ZXJuczoKKiBhIHNlYXNvbmFsIHBhdHRlcm4gYXQgMTIsIDI0LCAzNgoqIGFub3RoZXIgc2Vhc29uYWwgcGF0dGVybiBhdCAzLCA2LCA5LCAuLi4KCkxvb2tzIGxpa2Ugd2UgaGF2ZSBhIGNvbXBsZXggZHVhbC1zZWFzb25hbCBwYXR0ZXJuIGV2ZW4gYWZ0ZXIgZGlmZmVyZW5jaW5nLgoKYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQpnZ3RzZGlzcGxheShkaWZmKGx1c21lbGVjLGxhZyA9IDEyKSkKYGBgCgpUaGUgUEFDRiBwbG90IHNob3dzIGxhZ3MgYXQgMTIsIDI0LCAzNiB3aGljaCBhcmUgc2Vhc29uYWwgbGFncy4gUGVyaGFwcyBhbiBBUigzKSB0ZXJtIGZvciBzZWFzb25hbCBpcyBuZWVkZWQuIEZvciBub24tc2Vhc29uYWwgY29tcG9uZW50LCBjb25zaWRlcmluZyB0aGUgZHJvcCBpbiBBQ0YgdmFsdWVzLCB0aGUgbW9zdCBzaWduaWZpY2FudCBQQUNGIHZhbHVlIGlzIGZvciBsYWcgPSAxLiBQZXJoYXBzIGFuIEFSSU1BKDEsMCwwKSgwLDEsMylbMTJdIGlzIGEgZ29vZCBndWVzcy4KCiMjI0lkZW50aWZ5IGEgY291cGxlIG9mIEFSSU1BIG1vZGVscyB0aGF0IG1pZ2h0IGJlIHVzZWZ1bCBpbiBkZXNjcmliaW5nIHRoZSB0aW1lIHNlcmllcy4gV2hpY2ggb2YgeW91ciBtb2RlbHMgaXMgdGhlIGJlc3QgYWNjb3JkaW5nIHRvIHRoZWlyIEFJQyB2YWx1ZXM/CgpJdCBsb29rcyBsaWtlIEFSSU1BKDEsMCwxKSgxLDEsMSlbMTJdIGhhcyB0aGUgbG93ZXN0IEFJQyBvZiAtNDI0MC45OCwgYWZ0ZXIgc29tZSBpbnZlc3RpZ2F0aW9uLgpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CkFyaW1hKGx1c21lbGVjLCBvcmRlciA9IGMoMSwwLDApLCBzZWFzb25hbCA9IGxpc3Qob3JkZXI9YygwLDEsMyksIHBlcmlvZD0xMikpCkFyaW1hKGx1c21lbGVjLCBvcmRlciA9IGMoMSwwLDApLCBzZWFzb25hbCA9IGxpc3Qob3JkZXI9YygwLDEsMiksIHBlcmlvZD0xMikpCkFyaW1hKGx1c21lbGVjLCBvcmRlciA9IGMoMSwwLDApLCBzZWFzb25hbCA9IGxpc3Qob3JkZXI9YygxLDEsMiksIHBlcmlvZD0xMikpCkFyaW1hKGx1c21lbGVjLCBvcmRlciA9IGMoMSwwLDEpLCBzZWFzb25hbCA9IGxpc3Qob3JkZXI9YygwLDEsMyksIHBlcmlvZD0xMikpCkFyaW1hKGx1c21lbGVjLCBvcmRlciA9IGMoMSwwLDEpLCBzZWFzb25hbCA9IGxpc3Qob3JkZXI9YygxLDEsMSksIHBlcmlvZD0xMikpCmBgYAoKIyMjRXN0aW1hdGUgdGhlIHBhcmFtZXRlcnMgb2YgeW91ciBiZXN0IG1vZGVsIGFuZCBkbyBkaWFnbm9zdGljIHRlc3Rpbmcgb24gdGhlIHJlc2lkdWFscy4gRG8gdGhlIHJlc2lkdWFscyByZXNlbWJsZSB3aGl0ZSBub2lzZT8gSWYgbm90LCB0cnkgdG8gZmluZCBhbm90aGVyIEFSSU1BIG1vZGVsIHdoaWNoIGZpdHMgYmV0dGVyLgpgYGB7ciBvdXQud2lkdGg9ICc3MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9CmZpdF9zZWxlY3RlZCA8LSBBcmltYShsdXNtZWxlYywgb3JkZXIgPSBjKDEsMCwxKSwgc2Vhc29uYWwgPSBsaXN0KG9yZGVyPWMoMSwxLDMpLCBwZXJpb2Q9MTIpKQpmaXRfc2VsZWN0ZWQgJT4lIHN1bW1hcnkoKQpmaXRfc2VsZWN0ZWQgJT4lIGNoZWNrcmVzaWR1YWxzKCkKYGBgCgpSZXNpZHVhbHMgc2hvdyBhIGxlZnQgc2tldyBhbmQgdGhlIEFDRiBwbG90IHNob3dzIHNpZ25pZmljYW50IGxhZ3MgYXQgMSwyLDE0IGV0Yy4gVHJ5aW5nIG91dCBhbm90aGVyIG1vZGVsOiBhbiBBUklNQSgyLDAsMikoMSwxLDMpWzEyXSBpbXByb3ZlZCB0aGUgQUlDIHRvIC00MjY0LgoKYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQpmaXRfc2VsZWN0ZWQgPC0gQXJpbWEobHVzbWVsZWMsIG9yZGVyID0gYygyLDAsMiksIHNlYXNvbmFsID0gbGlzdChvcmRlcj1jKDEsMSwzKSwgcGVyaW9kPTEyKSkKZml0X3NlbGVjdGVkICU+JSBzdW1tYXJ5KCkKZml0X3NlbGVjdGVkICU+JSBjaGVja3Jlc2lkdWFscygpCmBgYAoKV2hhdCBkb2VzIGFuIGF1dG8uYXJpbWEgc2F5PyBBUklNQSgxLDEsMSkoMiwxLDEpWzEyXS4uLiBidXQgdGhlIEFJQ2MgaXMgd29yc2UgdGhhbiB0aGUgbW9kZWwgSSBjaG9zZS4KYGBge3Igb3V0LndpZHRoPSAnNzAlJywgZmlnLmFsaWduPSdjZW50ZXInfQpmaXRfc2VsZWN0ZWQgPC0gYXV0by5hcmltYShsdXNtZWxlYywgc3RlcHdpc2UgPSBGKQpmaXRfc2VsZWN0ZWQgJT4lIHN1bW1hcnkoKQpmaXRfc2VsZWN0ZWQgJT4lIGNoZWNrcmVzaWR1YWxzKCkKYGBgCgojIyNGb3JlY2FzdCB0aGUgbmV4dCAxNSB5ZWFycyBvZiBnZW5lcmF0aW9uIG9mIGVsZWN0cmljaXR5IGJ5IHRoZSBVLlMuIGVsZWN0cmljIGluZHVzdHJ5LiBHZXQgdGhlIGxhdGVzdCBmaWd1cmVzIGZyb20gaHR0cDovL2RhdGEuaXMvemdSV0NPIHRvIGNoZWNrIG9uIHRoZSBhY2N1cmFjeSBvZiB5b3VyIGZvcmVjYXN0cy4KClRoZSBmb3JlY2FzdGVkIG1vZGVsIHdvcmtzIHF1aXRlIHdlbGwhISBUaGUgZ3JlZW4gbGluZSBzaG93cyB0aGUgcmVhbCBkYXRhIGZvciB0aGUgcGFzdCBmZXcgeWVhcnMsIHdoaWxlIHRoZSByZWQgbGluZSBpcyBteSBmb3JlY2FzdHMuIEkgYW0gcXVpdGUgcGxlYXNlZC4KCmBgYHtyIG91dC53aWR0aD0gJzcwJScsIGZpZy5hbGlnbj0nY2VudGVyJ30KZml0X3NlbGVjdGVkIDwtIEFyaW1hKGx1c21lbGVjLCBvcmRlciA9IGMoMiwwLDIpLCAKICAgICAgICAgICAgICAgICAgICAgIHNlYXNvbmFsID0gbGlzdChvcmRlcj1jKDEsMSwzKSwgcGVyaW9kPTEyKSkKZm9yZWNhc3RlZCA8LSBmaXRfc2VsZWN0ZWQgJT4lIGZvcmVjYXN0KGg9MTUqMTIsIGxhbWJkYSA9IC0wLjQ3NzI0MDIpCmZvcmVjYXN0ZWQgPC0gdHMudW5pb24odXNtZWxlYywgZm9yZWNhc3RlZCRtZWFuKQpsb2FkKCJ+L0dEcml2ZSBOVS80MTMgVGltZSBTZXJpZXMvNDEzX1JQcm9qZWN0L25ld2RhdGEuUmRhdGEiKQpyZWFsX2RhdGEgPC0gdHMobmV3ZGF0YSR5LCBzdGFydD1jKDIwMTEsMSksIGZyZXF1ZW5jeSA9IDEyKQphdXRvcGxvdChmb3JlY2FzdGVkKSthdXRvbGF5ZXIocmVhbF9kYXRhKQphdXRvcGxvdChmb3JlY2FzdGVkKSthdXRvbGF5ZXIocmVhbF9kYXRhKSt4bGltKGMoMjAwOSwyMDE1KSkKYGBgCgojIyNIb3cgbWFueSB5ZWFycyBvZiBmb3JlY2FzdHMgZG8geW91IHRoaW5rIGFyZSBzdWZmaWNpZW50bHkgYWNjdXJhdGUgdG8gYmUgdXNhYmxlPwoKUHJvYmFibHkgYSB5ZWFyIG9yIHR3byB5ZWFycyBvdXQuIEJlY2F1c2UgZm9yZWNhc3RpbmcgYXNzdW1lcyB0aGUgdW5kZXJseWluZyBkYXRhIGdlbmVyYXRpbmcgcHJvY2VzcyByZW1haW5zIHVuY2hhbmdlZC4gSWYgdGhpcyBjaGFuZ2VzLCB0aGVuIHRoZSBtb2RlbCBpcyB1c2VsZXNzLg==